/*
    Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
    и других спецификаций для функционирования компактных приложений на языке
    Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

    Copyright © 2016–2017, 2019–2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package malik.emulator.microedition;

import java.io.*;
import javax.microedition.lcdui.*;
import malik.emulator.application.*;
import malik.emulator.fileformats.text.mapped.*;
import malik.emulator.io.cloud.*;
import malik.emulator.media.graphics.*;
import malik.emulator.util.*;

public class DeviceSettings extends Object
{
    public static final int DEVICE_KEY_1       = 0x31;
    public static final int DEVICE_KEY_2       = 0x32;
    public static final int DEVICE_KEY_3       = 0x33;
    public static final int DEVICE_KEY_4       = 0x34;
    public static final int DEVICE_KEY_5       = 0x35;
    public static final int DEVICE_KEY_6       = 0x36;
    public static final int DEVICE_KEY_7       = 0x37;
    public static final int DEVICE_KEY_8       = 0x38;
    public static final int DEVICE_KEY_9       = 0x39;
    public static final int DEVICE_KEY_STAR    = 0x2a;
    public static final int DEVICE_KEY_0       = 0x30;
    public static final int DEVICE_KEY_POUND   = 0x23;
    public static final int DEVICE_KEY_UP      = 0x26;
    public static final int DEVICE_KEY_DOWN    = 0x24;
    public static final int DEVICE_KEY_LEFT    = 0x28;
    public static final int DEVICE_KEY_RIGHT   = 0x29;
    public static final int DEVICE_KEY_SELECT  = 0x2b;
    public static final int DEVICE_KEY_SOFT1   = 0x25;
    public static final int DEVICE_KEY_SOFT2   = 0x27;
    public static final int DEVICE_KEY_CONSOLE = 0x2c;
    public static final int DEVICE_KEY_EXITAPP = 0x2e;
    public static final int FONT_STYLE_PLAIN       = 0x00;
    public static final int FONT_STYLE_BOLD        = 0x01;
    public static final int FONT_STYLE_ITALIC      = 0x02;
    public static final int FONT_STYLE_BOLD_ITALIC = 0x03;
    public static final int FONT_SIZE_MEDIUM = 0x00;
    public static final int FONT_SIZE_SMALL  = 0x01;
    public static final int FONT_SIZE_LARGE  = 0x02;
    private static final String MIDLET_PROPERTIES = "/midlet.properties";
    private static final String MIDLET_DESCRIPTOR = "/res/META-INF/MANIFEST.MF";

    private static final String[] FONT_KEYS;

    static {
        FONT_KEYS = new String[] {
            "font.medium.plain", "font.medium.bold", "font.medium.italic", "font.medium.bold.italic",
            "font.small.plain", "font.small.bold", "font.small.italic", "font.small.bold.italic",
            "font.large.plain", "font.large.bold", "font.large.italic", "font.large.bold.italic"
        };
    }

    public static int stringToKey(String keyName) {
        if(keyName == null) return 0;
        if("BACKSPACE".equals(keyName)) return KeyboardEvent.KEY_BACKSPACE;
        if("TAB".equals(keyName)) return KeyboardEvent.KEY_TAB;
        if("ENTER".equals(keyName)) return KeyboardEvent.KEY_ENTER;
        if("SHIFT".equals(keyName)) return KeyboardEvent.KEY_SHIFT;
        if("CTRL".equals(keyName)) return KeyboardEvent.KEY_CTRL;
        if("ALT".equals(keyName)) return KeyboardEvent.KEY_ALT;
        if("PAUSE_BREAK".equals(keyName)) return KeyboardEvent.KEY_PAUSE_BREAK;
        if("CAPS_LOCK".equals(keyName)) return KeyboardEvent.KEY_CAPS_LOCK;
        if("ESCAPE".equals(keyName)) return KeyboardEvent.KEY_ESCAPE;
        if("SPACE".equals(keyName)) return KeyboardEvent.KEY_SPACE;
        if("PAGE_UP".equals(keyName)) return KeyboardEvent.KEY_PAGE_UP;
        if("PAGE_DOWN".equals(keyName)) return KeyboardEvent.KEY_PAGE_DOWN;
        if("END".equals(keyName)) return KeyboardEvent.KEY_END;
        if("HOME".equals(keyName)) return KeyboardEvent.KEY_HOME;
        if("LEFT".equals(keyName)) return KeyboardEvent.KEY_LEFT;
        if("UP".equals(keyName)) return KeyboardEvent.KEY_UP;
        if("RIGHT".equals(keyName)) return KeyboardEvent.KEY_RIGHT;
        if("DOWN".equals(keyName)) return KeyboardEvent.KEY_DOWN;
        if("INSERT".equals(keyName)) return KeyboardEvent.KEY_INSERT;
        if("DELETE".equals(keyName)) return KeyboardEvent.KEY_DELETE;
        if("0".equals(keyName)) return KeyboardEvent.KEY_0;
        if("1".equals(keyName)) return KeyboardEvent.KEY_1;
        if("2".equals(keyName)) return KeyboardEvent.KEY_2;
        if("3".equals(keyName)) return KeyboardEvent.KEY_3;
        if("4".equals(keyName)) return KeyboardEvent.KEY_4;
        if("5".equals(keyName)) return KeyboardEvent.KEY_5;
        if("6".equals(keyName)) return KeyboardEvent.KEY_6;
        if("7".equals(keyName)) return KeyboardEvent.KEY_7;
        if("8".equals(keyName)) return KeyboardEvent.KEY_8;
        if("9".equals(keyName)) return KeyboardEvent.KEY_9;
        if("A".equals(keyName)) return KeyboardEvent.KEY_A;
        if("B".equals(keyName)) return KeyboardEvent.KEY_B;
        if("C".equals(keyName)) return KeyboardEvent.KEY_C;
        if("D".equals(keyName)) return KeyboardEvent.KEY_D;
        if("E".equals(keyName)) return KeyboardEvent.KEY_E;
        if("F".equals(keyName)) return KeyboardEvent.KEY_F;
        if("G".equals(keyName)) return KeyboardEvent.KEY_G;
        if("H".equals(keyName)) return KeyboardEvent.KEY_H;
        if("I".equals(keyName)) return KeyboardEvent.KEY_I;
        if("J".equals(keyName)) return KeyboardEvent.KEY_J;
        if("K".equals(keyName)) return KeyboardEvent.KEY_K;
        if("L".equals(keyName)) return KeyboardEvent.KEY_L;
        if("M".equals(keyName)) return KeyboardEvent.KEY_M;
        if("N".equals(keyName)) return KeyboardEvent.KEY_N;
        if("O".equals(keyName)) return KeyboardEvent.KEY_O;
        if("P".equals(keyName)) return KeyboardEvent.KEY_P;
        if("Q".equals(keyName)) return KeyboardEvent.KEY_Q;
        if("R".equals(keyName)) return KeyboardEvent.KEY_R;
        if("S".equals(keyName)) return KeyboardEvent.KEY_S;
        if("T".equals(keyName)) return KeyboardEvent.KEY_T;
        if("U".equals(keyName)) return KeyboardEvent.KEY_U;
        if("V".equals(keyName)) return KeyboardEvent.KEY_V;
        if("W".equals(keyName)) return KeyboardEvent.KEY_W;
        if("X".equals(keyName)) return KeyboardEvent.KEY_X;
        if("Y".equals(keyName)) return KeyboardEvent.KEY_Y;
        if("Z".equals(keyName)) return KeyboardEvent.KEY_Z;
        if("CONTEXT".equals(keyName)) return KeyboardEvent.KEY_CONTEXT;
        if("NUM_0".equals(keyName)) return KeyboardEvent.KEY_NUM_0;
        if("NUM_1".equals(keyName)) return KeyboardEvent.KEY_NUM_1;
        if("NUM_2".equals(keyName)) return KeyboardEvent.KEY_NUM_2;
        if("NUM_3".equals(keyName)) return KeyboardEvent.KEY_NUM_3;
        if("NUM_4".equals(keyName)) return KeyboardEvent.KEY_NUM_4;
        if("NUM_5".equals(keyName)) return KeyboardEvent.KEY_NUM_5;
        if("NUM_6".equals(keyName)) return KeyboardEvent.KEY_NUM_6;
        if("NUM_7".equals(keyName)) return KeyboardEvent.KEY_NUM_7;
        if("NUM_8".equals(keyName)) return KeyboardEvent.KEY_NUM_8;
        if("NUM_9".equals(keyName)) return KeyboardEvent.KEY_NUM_9;
        if("NUM_STAR".equals(keyName)) return KeyboardEvent.KEY_NUM_STAR;
        if("NUM_PLUS".equals(keyName)) return KeyboardEvent.KEY_NUM_PLUS;
        if("NUM_SEPARATOR".equals(keyName)) return KeyboardEvent.KEY_NUM_SEPARATOR;
        if("NUM_MINUS".equals(keyName)) return KeyboardEvent.KEY_NUM_MINUS;
        if("NUM_DECIMAL".equals(keyName)) return KeyboardEvent.KEY_NUM_DECIMAL;
        if("NUM_DIVIDE".equals(keyName)) return KeyboardEvent.KEY_NUM_DIVIDE;
        if("F1".equals(keyName)) return KeyboardEvent.KEY_F1;
        if("F2".equals(keyName)) return KeyboardEvent.KEY_F2;
        if("F3".equals(keyName)) return KeyboardEvent.KEY_F3;
        if("F4".equals(keyName)) return KeyboardEvent.KEY_F4;
        if("F5".equals(keyName)) return KeyboardEvent.KEY_F5;
        if("F6".equals(keyName)) return KeyboardEvent.KEY_F6;
        if("F7".equals(keyName)) return KeyboardEvent.KEY_F7;
        if("F8".equals(keyName)) return KeyboardEvent.KEY_F8;
        if("F9".equals(keyName)) return KeyboardEvent.KEY_F9;
        if("F10".equals(keyName)) return KeyboardEvent.KEY_F10;
        if("F11".equals(keyName)) return KeyboardEvent.KEY_F11;
        if("F12".equals(keyName)) return KeyboardEvent.KEY_F12;
        if("F13".equals(keyName)) return KeyboardEvent.KEY_F13;
        if("F14".equals(keyName)) return KeyboardEvent.KEY_F14;
        if("F15".equals(keyName)) return KeyboardEvent.KEY_F15;
        if("F16".equals(keyName)) return KeyboardEvent.KEY_F16;
        if("F17".equals(keyName)) return KeyboardEvent.KEY_F17;
        if("F18".equals(keyName)) return KeyboardEvent.KEY_F18;
        if("F19".equals(keyName)) return KeyboardEvent.KEY_F19;
        if("F20".equals(keyName)) return KeyboardEvent.KEY_F20;
        if("F21".equals(keyName)) return KeyboardEvent.KEY_F21;
        if("F22".equals(keyName)) return KeyboardEvent.KEY_F22;
        if("F23".equals(keyName)) return KeyboardEvent.KEY_F23;
        if("F24".equals(keyName)) return KeyboardEvent.KEY_F24;
        if("NUM_LOCK".equals(keyName)) return KeyboardEvent.KEY_NUM_LOCK;
        if("SCROLL_LOCK".equals(keyName)) return KeyboardEvent.KEY_SCROLL_LOCK;
        if("LSHIFT".equals(keyName)) return KeyboardEvent.KEY_LSHIFT;
        if("RSHIFT".equals(keyName)) return KeyboardEvent.KEY_RSHIFT;
        if("LCTRL".equals(keyName)) return KeyboardEvent.KEY_LCTRL;
        if("RCTRL".equals(keyName)) return KeyboardEvent.KEY_RCTRL;
        if("LALT".equals(keyName)) return KeyboardEvent.KEY_LALT;
        if("RALT".equals(keyName)) return KeyboardEvent.KEY_RALT;
        if("SEMICOLON".equals(keyName)) return KeyboardEvent.KEY_SEMICOLON;
        if("EQUAL".equals(keyName)) return KeyboardEvent.KEY_EQUAL;
        if("COMMA".equals(keyName)) return KeyboardEvent.KEY_COMMA;
        if("MINUS".equals(keyName)) return KeyboardEvent.KEY_MINUS;
        if("PERIOD".equals(keyName)) return KeyboardEvent.KEY_PERIOD;
        if("SLASH".equals(keyName)) return KeyboardEvent.KEY_SLASH;
        if("GRAVE".equals(keyName)) return KeyboardEvent.KEY_GRAVE;
        if("LBRACKET".equals(keyName)) return KeyboardEvent.KEY_LBRACKET;
        if("BACKSLASH".equals(keyName)) return KeyboardEvent.KEY_BACKSLASH;
        if("RBRACKET".equals(keyName)) return KeyboardEvent.KEY_RBRACKET;
        if("APOSTROPHE".equals(keyName)) return KeyboardEvent.KEY_APOSTROPHE;
        return 0;
    }

    public static String keyToString(int key) {
        switch(key)
        {
        default:
            return null;
        case KeyboardEvent.KEY_BACKSPACE:
            return "BACKSPACE";
        case KeyboardEvent.KEY_TAB:
            return "TAB";
        case KeyboardEvent.KEY_ENTER:
            return "ENTER";
        case KeyboardEvent.KEY_SHIFT:
            return "SHIFT";
        case KeyboardEvent.KEY_CTRL:
            return "CTRL";
        case KeyboardEvent.KEY_ALT:
            return "ALT";
        case KeyboardEvent.KEY_PAUSE_BREAK:
            return "PAUSE_BREAK";
        case KeyboardEvent.KEY_CAPS_LOCK:
            return "CAPS_LOCK";
        case KeyboardEvent.KEY_ESCAPE:
            return "ESCAPE";
        case KeyboardEvent.KEY_SPACE:
            return "SPACE";
        case KeyboardEvent.KEY_PAGE_UP:
            return "PAGE_UP";
        case KeyboardEvent.KEY_PAGE_DOWN:
            return "PAGE_DOWN";
        case KeyboardEvent.KEY_END:
            return "END";
        case KeyboardEvent.KEY_HOME:
            return "HOME";
        case KeyboardEvent.KEY_LEFT:
            return "LEFT";
        case KeyboardEvent.KEY_UP:
            return "UP";
        case KeyboardEvent.KEY_RIGHT:
            return "RIGHT";
        case KeyboardEvent.KEY_DOWN:
            return "DOWN";
        case KeyboardEvent.KEY_INSERT:
            return "INSERT";
        case KeyboardEvent.KEY_DELETE:
            return "DELETE";
        case KeyboardEvent.KEY_0:
            return "0";
        case KeyboardEvent.KEY_1:
            return "1";
        case KeyboardEvent.KEY_2:
            return "2";
        case KeyboardEvent.KEY_3:
            return "3";
        case KeyboardEvent.KEY_4:
            return "4";
        case KeyboardEvent.KEY_5:
            return "5";
        case KeyboardEvent.KEY_6:
            return "6";
        case KeyboardEvent.KEY_7:
            return "7";
        case KeyboardEvent.KEY_8:
            return "8";
        case KeyboardEvent.KEY_9:
            return "9";
        case KeyboardEvent.KEY_A:
            return "A";
        case KeyboardEvent.KEY_B:
            return "B";
        case KeyboardEvent.KEY_C:
            return "C";
        case KeyboardEvent.KEY_D:
            return "D";
        case KeyboardEvent.KEY_E:
            return "E";
        case KeyboardEvent.KEY_F:
            return "F";
        case KeyboardEvent.KEY_G:
            return "G";
        case KeyboardEvent.KEY_H:
            return "H";
        case KeyboardEvent.KEY_I:
            return "I";
        case KeyboardEvent.KEY_J:
            return "J";
        case KeyboardEvent.KEY_K:
            return "K";
        case KeyboardEvent.KEY_L:
            return "L";
        case KeyboardEvent.KEY_M:
            return "M";
        case KeyboardEvent.KEY_N:
            return "N";
        case KeyboardEvent.KEY_O:
            return "O";
        case KeyboardEvent.KEY_P:
            return "P";
        case KeyboardEvent.KEY_Q:
            return "Q";
        case KeyboardEvent.KEY_R:
            return "R";
        case KeyboardEvent.KEY_S:
            return "S";
        case KeyboardEvent.KEY_T:
            return "T";
        case KeyboardEvent.KEY_U:
            return "U";
        case KeyboardEvent.KEY_V:
            return "V";
        case KeyboardEvent.KEY_W:
            return "W";
        case KeyboardEvent.KEY_X:
            return "X";
        case KeyboardEvent.KEY_Y:
            return "Y";
        case KeyboardEvent.KEY_Z:
            return "Z";
        case KeyboardEvent.KEY_CONTEXT:
            return "CONTEXT";
        case KeyboardEvent.KEY_NUM_0:
            return "NUM_0";
        case KeyboardEvent.KEY_NUM_1:
            return "NUM_1";
        case KeyboardEvent.KEY_NUM_2:
            return "NUM_2";
        case KeyboardEvent.KEY_NUM_3:
            return "NUM_3";
        case KeyboardEvent.KEY_NUM_4:
            return "NUM_4";
        case KeyboardEvent.KEY_NUM_5:
            return "NUM_5";
        case KeyboardEvent.KEY_NUM_6:
            return "NUM_6";
        case KeyboardEvent.KEY_NUM_7:
            return "NUM_7";
        case KeyboardEvent.KEY_NUM_8:
            return "NUM_8";
        case KeyboardEvent.KEY_NUM_9:
            return "NUM_9";
        case KeyboardEvent.KEY_NUM_STAR:
            return "NUM_STAR";
        case KeyboardEvent.KEY_NUM_PLUS:
            return "NUM_PLUS";
        case KeyboardEvent.KEY_NUM_SEPARATOR:
            return "NUM_SEPARATOR";
        case KeyboardEvent.KEY_NUM_MINUS:
            return "NUM_MINUS";
        case KeyboardEvent.KEY_NUM_DECIMAL:
            return "NUM_DECIMAL";
        case KeyboardEvent.KEY_NUM_DIVIDE:
            return "NUM_DIVIDE";
        case KeyboardEvent.KEY_F1:
            return "F1";
        case KeyboardEvent.KEY_F2:
            return "F2";
        case KeyboardEvent.KEY_F3:
            return "F3";
        case KeyboardEvent.KEY_F4:
            return "F4";
        case KeyboardEvent.KEY_F5:
            return "F5";
        case KeyboardEvent.KEY_F6:
            return "F6";
        case KeyboardEvent.KEY_F7:
            return "F7";
        case KeyboardEvent.KEY_F8:
            return "F8";
        case KeyboardEvent.KEY_F9:
            return "F9";
        case KeyboardEvent.KEY_F10:
            return "F10";
        case KeyboardEvent.KEY_F11:
            return "F11";
        case KeyboardEvent.KEY_F12:
            return "F12";
        case KeyboardEvent.KEY_F13:
            return "F13";
        case KeyboardEvent.KEY_F14:
            return "F14";
        case KeyboardEvent.KEY_F15:
            return "F15";
        case KeyboardEvent.KEY_F16:
            return "F16";
        case KeyboardEvent.KEY_F17:
            return "F17";
        case KeyboardEvent.KEY_F18:
            return "F18";
        case KeyboardEvent.KEY_F19:
            return "F19";
        case KeyboardEvent.KEY_F20:
            return "F20";
        case KeyboardEvent.KEY_F21:
            return "F21";
        case KeyboardEvent.KEY_F22:
            return "F22";
        case KeyboardEvent.KEY_F23:
            return "F23";
        case KeyboardEvent.KEY_F24:
            return "F24";
        case KeyboardEvent.KEY_NUM_LOCK:
            return "NUM_LOCK";
        case KeyboardEvent.KEY_SCROLL_LOCK:
            return "SCROLL_LOCK";
        case KeyboardEvent.KEY_LSHIFT:
            return "LSHIFT";
        case KeyboardEvent.KEY_RSHIFT:
            return "RSHIFT";
        case KeyboardEvent.KEY_LCTRL:
            return "LCTRL";
        case KeyboardEvent.KEY_RCTRL:
            return "RCTRL";
        case KeyboardEvent.KEY_LALT:
            return "LALT";
        case KeyboardEvent.KEY_RALT:
            return "RALT";
        case KeyboardEvent.KEY_SEMICOLON:
            return "SEMICOLON";
        case KeyboardEvent.KEY_EQUAL:
            return "EQUAL";
        case KeyboardEvent.KEY_COMMA:
            return "COMMA";
        case KeyboardEvent.KEY_MINUS:
            return "MINUS";
        case KeyboardEvent.KEY_PERIOD:
            return "PERIOD";
        case KeyboardEvent.KEY_SLASH:
            return "SLASH";
        case KeyboardEvent.KEY_GRAVE:
            return "GRAVE";
        case KeyboardEvent.KEY_LBRACKET:
            return "LBRACKET";
        case KeyboardEvent.KEY_BACKSLASH:
            return "BACKSLASH";
        case KeyboardEvent.KEY_RBRACKET:
            return "RBRACKET";
        case KeyboardEvent.KEY_APOSTROPHE:
            return "APOSTROPHE";
        }
    }

    protected static boolean readPropertyAsBoolean(MappedTextDecoder source, String key, boolean defaultValue) {
        String value;
        if(source == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsBoolean: аргумент source равен нулевой ссылке.");
        }
        if(key == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsBoolean: аргумент key равен нулевой ссылке.");
        }
        value = source.get(key);
        return "false".equalsIgnoreCase(value) ? false : "true".equalsIgnoreCase(value) ? true : defaultValue;
    }

    protected static double readPropertyAsDouble(MappedTextDecoder source, String key, double defaultValue) {
        double result;
        if(source == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsDouble: аргумент source равен нулевой ссылке.");
        }
        if(key == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsDouble: аргумент key равен нулевой ссылке.");
        }
        try
        {
            String value;
            result = (value = source.get(key)) == null ? defaultValue : Double.parseDouble(value);
        }
        catch(NumberFormatException e)
        {
            result = defaultValue;
        }
        return result;
    }

    protected static int readPropertyAsInt(MappedTextDecoder source, String key, int defaultValue) {
        int result;
        if(source == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsInt: аргумент source равен нулевой ссылке.");
        }
        if(key == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsInt: аргумент key равен нулевой ссылке.");
        }
        try
        {
            String value;
            if((value = source.get(key)) == null)
            {
                result = defaultValue;
            }
            else if(value.regionMatches(true, 0, "0x", 0, 2))
            {
                result = Integer.parseInt(value.substring(2), 16);
            }
            else if(value.regionMatches(true, 0, "+0x", 0, 3))
            {
                result = Integer.parseInt(value.substring(3), 16);
            }
            else if(value.regionMatches(true, 0, "-0x", 0, 3))
            {
                result = Integer.parseInt("-".concat(value.substring(3)), 16);
            }
            else
            {
                result = Integer.parseInt(value, 10);
            }
        }
        catch(NumberFormatException e)
        {
            result = defaultValue;
        }
        return result;
    }

    protected static int readPropertyAsKey(MappedTextDecoder source, String key, int defaultKey) {
        int result;
        if(source == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsKey: аргумент source равен нулевой ссылке.");
        }
        if(key == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsKey: аргумент key равен нулевой ссылке.");
        }
        return (result = stringToKey(source.get(key))) == 0 ? defaultKey : result;
    }

    protected static long readPropertyAsLong(MappedTextDecoder source, String key, long defaultValue) {
        long result;
        if(source == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsLong: аргумент source равен нулевой ссылке.");
        }
        if(key == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsLong: аргумент key равен нулевой ссылке.");
        }
        try
        {
            String value;
            if((value = source.get(key)) == null)
            {
                result = defaultValue;
            }
            else if(value.regionMatches(true, 0, "0x", 0, 2))
            {
                result = Long.parseLong(value.substring(2), 16);
            }
            else if(value.regionMatches(true, 0, "+0x", 0, 3))
            {
                result = Long.parseLong(value.substring(3), 16);
            }
            else if(value.regionMatches(true, 0, "-0x", 0, 3))
            {
                result = Long.parseLong("-".concat(value.substring(3)), 16);
            }
            else
            {
                result = Long.parseLong(value, 10);
            }
        }
        catch(NumberFormatException e)
        {
            result = defaultValue;
        }
        return result;
    }

    protected static String readPropertyAsString(MappedTextDecoder source, String key, String defaultValue) {
        String value;
        if(source == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsString: аргумент source равен нулевой ссылке.");
        }
        if(key == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsString: аргумент key равен нулевой ссылке.");
        }
        return (value = source.get(key)) == null ? defaultValue : value;
    }

    protected static SystemFont readPropertyAsRasterFont(MappedTextDecoder source, String key, SystemFont defaultFont) {
        String value;
        if(source == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsRasterFont: аргумент source равен нулевой ссылке.");
        }
        if(key == null)
        {
            throw new NullPointerException("DeviceSettings.readPropertyAsRasterFont: аргумент key равен нулевой ссылке.");
        }
        return (value = source.get(key)) == null ? defaultFont : SystemFont.get(value);
    }

    private boolean keyRepeatedEvent;
    private int keyUsedAs1;
    private int keyUsedAs2;
    private int keyUsedAs3;
    private int keyUsedAs4;
    private int keyUsedAs5;
    private int keyUsedAs6;
    private int keyUsedAs7;
    private int keyUsedAs8;
    private int keyUsedAs9;
    private int keyUsedAsStar;
    private int keyUsedAs0;
    private int keyUsedAsPound;
    private int keyUsedAsUp;
    private int keyUsedAsDown;
    private int keyUsedAsLeft;
    private int keyUsedAsRight;
    private int keyUsedAsSelect;
    private int keyUsedAsSoft1;
    private int keyUsedAsSoft2;
    private int keyUsedAsConsole;
    private int keyUsedAsExitApp;
    private int keyCodeForUp;
    private int keyCodeForDown;
    private int keyCodeForLeft;
    private int keyCodeForRight;
    private int keyCodeForSelect;
    private int keyCodeForSoft1;
    private int keyCodeForSoft2;
    private int maximumFrequency;
    private int minimumPeriod;
    private final SystemFont[] fonts;
    private final MappedTextDecoder midletProperties;
    private final AttributedTextDecoder midletDescriptor;

    public DeviceSettings() {
        this.fonts = new SystemFont[FONT_KEYS.length];
        this.midletProperties = new PropertiesDecoder();
        this.midletDescriptor = new ManifestDecoder();
    }

    public void setKeyUsedAs(int deviceKey, int key) {
        switch(deviceKey)
        {
        case DEVICE_KEY_1:
            keyUsedAs1 = key;
            break;
        case DEVICE_KEY_2:
            keyUsedAs2 = key;
            break;
        case DEVICE_KEY_3:
            keyUsedAs3 = key;
            break;
        case DEVICE_KEY_4:
            keyUsedAs4 = key;
            break;
        case DEVICE_KEY_5:
            keyUsedAs5 = key;
            break;
        case DEVICE_KEY_6:
            keyUsedAs6 = key;
            break;
        case DEVICE_KEY_7:
            keyUsedAs7 = key;
            break;
        case DEVICE_KEY_8:
            keyUsedAs8 = key;
            break;
        case DEVICE_KEY_9:
            keyUsedAs9 = key;
            break;
        case DEVICE_KEY_STAR:
            keyUsedAsStar = key;
            break;
        case DEVICE_KEY_0:
            keyUsedAs0 = key;
            break;
        case DEVICE_KEY_POUND:
            keyUsedAsPound = key;
            break;
        case DEVICE_KEY_UP:
            keyUsedAsUp = key;
            break;
        case DEVICE_KEY_DOWN:
            keyUsedAsDown = key;
            break;
        case DEVICE_KEY_LEFT:
            keyUsedAsLeft = key;
            break;
        case DEVICE_KEY_RIGHT:
            keyUsedAsRight = key;
            break;
        case DEVICE_KEY_SELECT:
            keyUsedAsSelect = key;
            break;
        case DEVICE_KEY_SOFT1:
            keyUsedAsSoft1 = key;
            break;
        case DEVICE_KEY_SOFT2:
            keyUsedAsSoft2 = key;
            break;
        case DEVICE_KEY_CONSOLE:
            keyUsedAsConsole = key;
            break;
        case DEVICE_KEY_EXITAPP:
            keyUsedAsExitApp = key;
            break;
        default:
            throw new IllegalArgumentException("DeviceSettings.setKeyUsedAs: аргумент deviceKey имеет недопустимое значение.");
        }
    }

    public void setKeyCodeFor(int deviceKey, int keyCode) {
        switch(deviceKey)
        {
        case DEVICE_KEY_UP:
            keyCodeForUp = keyCode;
            break;
        case DEVICE_KEY_DOWN:
            keyCodeForDown = keyCode;
            break;
        case DEVICE_KEY_LEFT:
            keyCodeForLeft = keyCode;
            break;
        case DEVICE_KEY_RIGHT:
            keyCodeForRight = keyCode;
            break;
        case DEVICE_KEY_SELECT:
            keyCodeForSelect = keyCode;
            break;
        case DEVICE_KEY_SOFT1:
            keyCodeForSoft1 = keyCode;
            break;
        case DEVICE_KEY_SOFT2:
            keyCodeForSoft2 = keyCode;
            break;
        case DEVICE_KEY_1:
        case DEVICE_KEY_2:
        case DEVICE_KEY_3:
        case DEVICE_KEY_4:
        case DEVICE_KEY_5:
        case DEVICE_KEY_6:
        case DEVICE_KEY_7:
        case DEVICE_KEY_8:
        case DEVICE_KEY_9:
        case DEVICE_KEY_STAR:
        case DEVICE_KEY_0:
        case DEVICE_KEY_POUND:
            throw new IllegalStateException("DeviceSettings.setKeyCodeFor: для цифровых кнопок, звёздочки и решётки нельзя изменить код клавиши.");
        case DEVICE_KEY_CONSOLE:
        case DEVICE_KEY_EXITAPP:
            throw new IllegalStateException("DeviceSettings.setKeyCodeFor: для системных кнопок нельзя изменить код клавиши.");
        default:
            throw new IllegalArgumentException("DeviceSettings.setKeyCodeFor: аргумент deviceKey имеет недопустимое значение.");
        }
    }

    public void setFont(int style, int size, SystemFont font) {
        if(style < 0 || style > 3)
        {
            throw new IllegalArgumentException("DeviceSettings.setFont: аргумент style имеет недопустимое значение.");
        }
        if(size < 0 || size > 2)
        {
            throw new IllegalArgumentException("DeviceSettings.setFont: аргумент size имеет недопустимое значение.");
        }
        fonts[size << 2 | style] = font;
    }

    public void setUserSetting(String settingName, Object settingValue) {
        throw new IllegalArgumentException((new StringBuilder()).append("DeviceSettings.setUserSetting: неизвестная настройка ").append(settingName).append('.').toString());
    }

    public int keyCodeToGameAction(int keyCode) {
        if(keyCode == keyCodeForUp) return Canvas.UP;
        if(keyCode == keyCodeForDown) return Canvas.DOWN;
        if(keyCode == keyCodeForLeft) return Canvas.LEFT;
        if(keyCode == keyCodeForRight) return Canvas.RIGHT;
        if(keyCode == keyCodeForSelect) return Canvas.FIRE;
        if(keyCode == keyCodeForSoft1) return 0;
        if(keyCode == keyCodeForSoft2) return 0;
        switch(keyCode)
        {
        case DEVICE_KEY_1:
            return Canvas.GAME_A;
        case DEVICE_KEY_2:
            return Canvas.UP;
        case DEVICE_KEY_3:
            return Canvas.GAME_B;
        case DEVICE_KEY_4:
            return Canvas.LEFT;
        case DEVICE_KEY_5:
            return Canvas.FIRE;
        case DEVICE_KEY_6:
            return Canvas.RIGHT;
        case DEVICE_KEY_7:
            return Canvas.GAME_C;
        case DEVICE_KEY_8:
            return Canvas.DOWN;
        case DEVICE_KEY_9:
            return Canvas.GAME_D;
        case DEVICE_KEY_STAR:
        case DEVICE_KEY_0:
        case DEVICE_KEY_POUND:
            return 0;
        default:
            throw new IllegalArgumentException((new StringBuilder()).append("DeviceSettings.keyCodeToGameAction: несуществующий код клавиши ").append(keyCode).append('.').toString());
        }
    }

    public int gameActionToKeyCode(int gameAction) {
        switch(gameAction)
        {
        case Canvas.UP:
            return DEVICE_KEY_2;
        case Canvas.DOWN:
            return DEVICE_KEY_8;
        case Canvas.LEFT:
            return DEVICE_KEY_4;
        case Canvas.RIGHT:
            return DEVICE_KEY_6;
        case Canvas.FIRE:
            return DEVICE_KEY_5;
        case Canvas.GAME_A:
            return DEVICE_KEY_1;
        case Canvas.GAME_B:
            return DEVICE_KEY_3;
        case Canvas.GAME_C:
            return DEVICE_KEY_7;
        case Canvas.GAME_D:
            return DEVICE_KEY_9;
        default:
            throw new IllegalArgumentException((new StringBuilder()).append("DeviceSettings.gameActionToKeyCode: несуществующее игровое действие ").append(gameAction).append('.').toString());
        }
    }

    public int getKeyUsedAs(int deviceKey) {
        switch(deviceKey)
        {
        case DEVICE_KEY_1:
            return keyUsedAs1;
        case DEVICE_KEY_2:
            return keyUsedAs2;
        case DEVICE_KEY_3:
            return keyUsedAs3;
        case DEVICE_KEY_4:
            return keyUsedAs4;
        case DEVICE_KEY_5:
            return keyUsedAs5;
        case DEVICE_KEY_6:
            return keyUsedAs6;
        case DEVICE_KEY_7:
            return keyUsedAs7;
        case DEVICE_KEY_8:
            return keyUsedAs8;
        case DEVICE_KEY_9:
            return keyUsedAs9;
        case DEVICE_KEY_STAR:
            return keyUsedAsStar;
        case DEVICE_KEY_0:
            return keyUsedAs0;
        case DEVICE_KEY_POUND:
            return keyUsedAsPound;
        case DEVICE_KEY_UP:
            return keyUsedAsUp;
        case DEVICE_KEY_DOWN:
            return keyUsedAsDown;
        case DEVICE_KEY_LEFT:
            return keyUsedAsLeft;
        case DEVICE_KEY_RIGHT:
            return keyUsedAsRight;
        case DEVICE_KEY_SELECT:
            return keyUsedAsSelect;
        case DEVICE_KEY_SOFT1:
            return keyUsedAsSoft1;
        case DEVICE_KEY_SOFT2:
            return keyUsedAsSoft2;
        case DEVICE_KEY_CONSOLE:
            return keyUsedAsConsole;
        case DEVICE_KEY_EXITAPP:
            return keyUsedAsExitApp;
        default:
            throw new IllegalArgumentException("DeviceSettings.getKeyUsedAs: аргумент deviceKey имеет недопустимое значение.");
        }
    }

    public int getKeyCodeFor(int deviceKey) {
        switch(deviceKey)
        {
        case DEVICE_KEY_1:
        case DEVICE_KEY_2:
        case DEVICE_KEY_3:
        case DEVICE_KEY_4:
        case DEVICE_KEY_5:
        case DEVICE_KEY_6:
        case DEVICE_KEY_7:
        case DEVICE_KEY_8:
        case DEVICE_KEY_9:
        case DEVICE_KEY_STAR:
        case DEVICE_KEY_0:
        case DEVICE_KEY_POUND:
            return deviceKey;
        case DEVICE_KEY_UP:
            return keyCodeForUp;
        case DEVICE_KEY_DOWN:
            return keyCodeForDown;
        case DEVICE_KEY_LEFT:
            return keyCodeForLeft;
        case DEVICE_KEY_RIGHT:
            return keyCodeForRight;
        case DEVICE_KEY_SELECT:
            return keyCodeForSelect;
        case DEVICE_KEY_SOFT1:
            return keyCodeForSoft1;
        case DEVICE_KEY_SOFT2:
            return keyCodeForSoft2;
        case DEVICE_KEY_CONSOLE:
        case DEVICE_KEY_EXITAPP:
            throw new IllegalStateException("DeviceSettings.getKeyCodeFor: для системных кнопок не существует код клавиши.");
        default:
            throw new IllegalArgumentException("DeviceSettings.getKeyCodeFor: аргумент deviceKey имеет недопустимое значение.");
        }
    }

    public int getKeyCode(int key) {
        if(key == keyUsedAs1) return DEVICE_KEY_1;
        if(key == keyUsedAs2) return DEVICE_KEY_2;
        if(key == keyUsedAs3) return DEVICE_KEY_3;
        if(key == keyUsedAs4) return DEVICE_KEY_4;
        if(key == keyUsedAs5) return DEVICE_KEY_5;
        if(key == keyUsedAs6) return DEVICE_KEY_6;
        if(key == keyUsedAs7) return DEVICE_KEY_7;
        if(key == keyUsedAs8) return DEVICE_KEY_8;
        if(key == keyUsedAs9) return DEVICE_KEY_9;
        if(key == keyUsedAsStar) return DEVICE_KEY_STAR;
        if(key == keyUsedAs0) return DEVICE_KEY_0;
        if(key == keyUsedAsPound) return DEVICE_KEY_POUND;
        if(key == keyUsedAsUp) return keyCodeForUp;
        if(key == keyUsedAsDown) return keyCodeForDown;
        if(key == keyUsedAsLeft) return keyCodeForLeft;
        if(key == keyUsedAsRight) return keyCodeForRight;
        if(key == keyUsedAsSelect) return keyCodeForSelect;
        if(key == keyUsedAsSoft1) return keyCodeForSoft1;
        if(key == keyUsedAsSoft2) return keyCodeForSoft2;
        return 0;
    }

    public String getKeyName(int keyCode) {
        if(keyCode == keyCodeForUp) return "\u2191";
        if(keyCode == keyCodeForDown) return "\u2193";
        if(keyCode == keyCodeForLeft) return "\u2190";
        if(keyCode == keyCodeForRight) return "\u2192";
        if(keyCode == keyCodeForSelect) return "Центральная клавиша выбора";
        if(keyCode == keyCodeForSoft1) return "Левая клавиша выбора";
        if(keyCode == keyCodeForSoft2) return "Правая клавиша выбора";
        switch(keyCode)
        {
        case DEVICE_KEY_1:
            return "1";
        case DEVICE_KEY_2:
            return "2";
        case DEVICE_KEY_3:
            return "3";
        case DEVICE_KEY_4:
            return "4";
        case DEVICE_KEY_5:
            return "5";
        case DEVICE_KEY_6:
            return "6";
        case DEVICE_KEY_7:
            return "7";
        case DEVICE_KEY_8:
            return "8";
        case DEVICE_KEY_9:
            return "9";
        case DEVICE_KEY_STAR:
            return "*";
        case DEVICE_KEY_0:
            return "0";
        case DEVICE_KEY_POUND:
            return "#";
        default:
            throw new IllegalArgumentException((new StringBuilder()).append("DeviceSettings.getKeyName: несуществующий код клавиши ").append(keyCode).append('.').toString());
        }
    }

    public SystemFont getFont(int style, int size) {
        if(style < 0 || style > 3)
        {
            throw new IllegalArgumentException("DeviceSettings.getFont: аргумент style имеет недопустимое значение.");
        }
        if(size < 0 || size > 2)
        {
            throw new IllegalArgumentException("DeviceSettings.getFont: аргумент size имеет недопустимое значение.");
        }
        return fonts[size << 2 | style];
    }

    public Object getUserSetting(String settingName) {
        throw new IllegalArgumentException((new StringBuilder()).append("DeviceSettings.getUserSetting: неизвестная настройка ").append(settingName).append('.').toString());
    }

    public final void loadMIDletDescriptor() {
        try
        {
            InputStream stream = CloudFileSystem.instance.openFileForRead(MIDLET_DESCRIPTOR);
            try
            {
                midletDescriptor.loadFromInputStream(stream);
            }
            finally
            {
                stream.close();
            }
        }
        catch(IOException e)
        {
            System.err.println("Не удалось загрузить дескриптор мидлета из " + MIDLET_DESCRIPTOR + ". Подробности следуют ниже.");
            e.printRealStackTrace();
        }
    }

    public final void loadMIDletDescriptor(InputStream stream) {
        if(stream == null)
        {
            throw new NullPointerException("DeviceSettings.loadMIDletDescriptor: аргумент stream равен нулевой ссылке.");
        }
        try
        {
            midletDescriptor.loadFromInputStream(stream);
        }
        catch(IOException e)
        {
            System.err.println((new StringBuilder()).
                append("Не удалось загрузить дескриптор мидлета из ").append(stream instanceof HandleInputStream ? ((HandleInputStream) stream).getFileName() : stream.getClass().getCanonicalName()).
                append(". Подробности следуют ниже.").
            toString());
            e.printRealStackTrace();
        }
    }

    public final void loadMIDletProperties() {
        MappedTextDecoder source = midletProperties;
        try
        {
            InputStream stream = CloudFileSystem.instance.openFileForRead(MIDLET_PROPERTIES);
            try
            {
                source.loadFromInputStream(stream);
            }
            finally
            {
                stream.close();
            }
            loadFrom(source);
        }
        catch(IOException e)
        {
            System.err.println("Не удалось загрузить настройки мидлета из " + MIDLET_PROPERTIES + ". Использованы настройки по умолчанию. Подробности следуют ниже.");
            e.printRealStackTrace();
            loadDefaults();
        }
    }

    public final void loadMIDletProperties(InputStream stream) {
        if(stream == null)
        {
            throw new NullPointerException("DeviceSettings.loadMIDletProperties: аргумент stream равен нулевой ссылке.");
        }
        try
        {
            MappedTextDecoder source;
            (source = midletProperties).loadFromInputStream(stream);
            loadFrom(source);
        }
        catch(IOException e)
        {
            System.err.println((new StringBuilder()).
                append("Не удалось загрузить настройки мидлета из ").append(stream instanceof HandleInputStream ? ((HandleInputStream) stream).getFileName() : stream.getClass().getCanonicalName()).
                append(". Использованы настройки по умолчанию. Подробности следуют ниже.").
            toString());
            e.printRealStackTrace();
            loadDefaults();
        }
    }

    public final void loadMIDletProperties(MappedTextDecoder source) {
        if(source == null)
        {
            throw new NullPointerException("DeviceSettings.loadMIDletProperties: аргумент source равен нулевой ссылке.");
        }
        loadFrom(source);
    }

    public final void saveMIDletProperties() {
        PrintStream printer = new PrintStream(new FileOutputStream(MIDLET_PROPERTIES));
        try
        {
            saveTo(printer);
        }
        finally
        {
            printer.close();
        }
    }

    public final void saveMIDletProperties(OutputStream stream) {
        if(stream == null)
        {
            throw new NullPointerException("DeviceSettings.saveMIDletProperties: аргумент stream равен нулевой ссылке.");
        }
        saveTo(stream instanceof PrintStream ? (PrintStream) stream : new PrintStream(stream));
    }

    public final void saveMIDletProperties(PrintStream printer) {
        if(printer == null)
        {
            throw new NullPointerException("DeviceSettings.saveMIDletProperties: аргумент printer равен нулевой ссылке.");
        }
        saveTo(printer);
    }

    public final void setEnableStackTrace(boolean enableStackTrace) {
        ThrowableStackTrace.enable(enableStackTrace);
    }

    public final void setKeyRepeatedEvent(boolean keyRepeatedEvent) {
        this.keyRepeatedEvent = keyRepeatedEvent;
    }

    public final void setMaximumFrequency(int maximumFrequency) {
        if(maximumFrequency < 1) maximumFrequency = 1;
        if(maximumFrequency > 99) maximumFrequency = 99;
        this.maximumFrequency = maximumFrequency;
        this.minimumPeriod = (1000 / maximumFrequency) + (1000 % maximumFrequency > 0 ? 1 : 0);
    }

    public final boolean isEnableStackTrace() {
        return ThrowableStackTrace.enabled();
    }

    public final boolean isKeyRepeatedEvent() {
        return keyRepeatedEvent;
    }

    public final int getMaximumFrequency() {
        return maximumFrequency;
    }

    public final int getMinimumPeriod() {
        return minimumPeriod;
    }

    public final String[] getMIDletAttributes(String key) {
        return midletDescriptor.getAttributes(key);
    }

    public final String getMIDletProperty(String key) {
        return midletDescriptor.get(key);
    }

    protected void defaultMIDletProperties() {
    }

    protected void readMIDletProperties(MappedTextDecoder source) {
    }

    protected void writeMIDletProperties(PrintStream printer) {
    }

    private void loadDefaults() {
        SystemFont[] f;
        Array.fill(f = fonts, 0, f.length, SystemFont.getDefault());
        setEnableStackTrace(true);
        setKeyRepeatedEvent(true);
        setMaximumFrequency(25);
        keyUsedAs1 = KeyboardEvent.KEY_NUM_7;
        keyUsedAs2 = KeyboardEvent.KEY_NUM_8;
        keyUsedAs3 = KeyboardEvent.KEY_NUM_9;
        keyUsedAs4 = KeyboardEvent.KEY_NUM_4;
        keyUsedAs5 = KeyboardEvent.KEY_NUM_5;
        keyUsedAs6 = KeyboardEvent.KEY_NUM_6;
        keyUsedAs7 = KeyboardEvent.KEY_NUM_1;
        keyUsedAs8 = KeyboardEvent.KEY_NUM_2;
        keyUsedAs9 = KeyboardEvent.KEY_NUM_3;
        keyUsedAsStar = KeyboardEvent.KEY_NUM_STAR;
        keyUsedAs0 = KeyboardEvent.KEY_NUM_0;
        keyUsedAsPound = KeyboardEvent.KEY_NUM_DIVIDE;
        keyUsedAsUp = KeyboardEvent.KEY_UP;
        keyUsedAsDown = KeyboardEvent.KEY_DOWN;
        keyUsedAsLeft = KeyboardEvent.KEY_LEFT;
        keyUsedAsRight = KeyboardEvent.KEY_RIGHT;
        keyUsedAsSelect = KeyboardEvent.KEY_ENTER;
        keyUsedAsSoft1 = KeyboardEvent.KEY_F1;
        keyUsedAsSoft2 = KeyboardEvent.KEY_F2;
        keyUsedAsConsole = KeyboardEvent.KEY_F3;
        keyUsedAsExitApp = KeyboardEvent.KEY_F4;
        keyCodeForUp = -1;
        keyCodeForDown = -2;
        keyCodeForLeft = -3;
        keyCodeForRight = -4;
        keyCodeForSelect = -5;
        keyCodeForSoft1 = -6;
        keyCodeForSoft2 = -7;
        defaultMIDletProperties();
    }

    private void loadFrom(MappedTextDecoder source) {
        SystemFont[] f = fonts;
        SystemFont d = SystemFont.getDefault();
        for(int i = f.length; i-- > 0; f[i] = readPropertyAsRasterFont(source, FONT_KEYS[i], d));
        setEnableStackTrace(readPropertyAsBoolean(source, "system.ENABLE_STACK_TRACE", true));
        setKeyRepeatedEvent(readPropertyAsBoolean(source, "system.KEY_REPEATED_EVENT", true));
        setMaximumFrequency(readPropertyAsInt(source, "system.MAXIMUM_FREQUENCY", 25));
        keyUsedAs1 = readPropertyAsKey(source, "key.1", KeyboardEvent.KEY_NUM_7);
        keyUsedAs2 = readPropertyAsKey(source, "key.2", KeyboardEvent.KEY_NUM_8);
        keyUsedAs3 = readPropertyAsKey(source, "key.3", KeyboardEvent.KEY_NUM_9);
        keyUsedAs4 = readPropertyAsKey(source, "key.4", KeyboardEvent.KEY_NUM_4);
        keyUsedAs5 = readPropertyAsKey(source, "key.5", KeyboardEvent.KEY_NUM_5);
        keyUsedAs6 = readPropertyAsKey(source, "key.6", KeyboardEvent.KEY_NUM_6);
        keyUsedAs7 = readPropertyAsKey(source, "key.7", KeyboardEvent.KEY_NUM_1);
        keyUsedAs8 = readPropertyAsKey(source, "key.8", KeyboardEvent.KEY_NUM_2);
        keyUsedAs9 = readPropertyAsKey(source, "key.9", KeyboardEvent.KEY_NUM_3);
        keyUsedAsStar = readPropertyAsKey(source, "key.ASTERISK", KeyboardEvent.KEY_NUM_STAR);
        keyUsedAs0 = readPropertyAsKey(source, "key.0", KeyboardEvent.KEY_NUM_0);
        keyUsedAsPound = readPropertyAsKey(source, "key.POUND", KeyboardEvent.KEY_NUM_DIVIDE);
        keyUsedAsUp = readPropertyAsKey(source, "key.UP", KeyboardEvent.KEY_UP);
        keyUsedAsDown = readPropertyAsKey(source, "key.DOWN", KeyboardEvent.KEY_DOWN);
        keyUsedAsLeft = readPropertyAsKey(source, "key.LEFT", KeyboardEvent.KEY_LEFT);
        keyUsedAsRight = readPropertyAsKey(source, "key.RIGHT", KeyboardEvent.KEY_RIGHT);
        keyUsedAsSelect = readPropertyAsKey(source, "key.SELECT", KeyboardEvent.KEY_ENTER);
        keyUsedAsSoft1 = readPropertyAsKey(source, "key.SOFT1", KeyboardEvent.KEY_F1);
        keyUsedAsSoft2 = readPropertyAsKey(source, "key.SOFT2", KeyboardEvent.KEY_F2);
        keyUsedAsConsole = readPropertyAsKey(source, "key.CONSOLE", KeyboardEvent.KEY_F3);
        keyUsedAsExitApp = readPropertyAsKey(source, "key.EXITAPP", KeyboardEvent.KEY_F4);
        keyCodeForUp = readPropertyAsInt(source, "key.code.UP", -1);
        keyCodeForDown = readPropertyAsInt(source, "key.code.DOWN", -2);
        keyCodeForLeft = readPropertyAsInt(source, "key.code.LEFT", -3);
        keyCodeForRight = readPropertyAsInt(source, "key.code.RIGHT", -4);
        keyCodeForSelect = readPropertyAsInt(source, "key.code.SELECT", -5);
        keyCodeForSoft1 = readPropertyAsInt(source, "key.code.SOFT1", -6);
        keyCodeForSoft2 = readPropertyAsInt(source, "key.code.SOFT2", -7);
        readMIDletProperties(source);
    }

    private void saveTo(PrintStream printer) {
        printer.println();
        printer.println("# Настройки мидлета (можно менять, не запуская сам мидлет)");
        printer.println("# Этот файл перезаписывается каждый раз, когда вы сохраняете");
        printer.println("# настройки, поэтому НЕ дописывайте в него какую-либо информацию.");
        printer.println();
        printer.println("system.ENABLE_STACK_TRACE = ".concat(isEnableStackTrace() ? "true" : "false"));
        printer.println("system.KEY_REPEATED_EVENT = ".concat(isKeyRepeatedEvent() ? "true" : "false"));
        printer.println("system.MAXIMUM_FREQUENCY = ".concat(Integer.toString(getMaximumFrequency())));
        printer.println();
        printer.println("key.1 = ".concat(keyToString(keyUsedAs1)));
        printer.println("key.2 = ".concat(keyToString(keyUsedAs2)));
        printer.println("key.3 = ".concat(keyToString(keyUsedAs3)));
        printer.println("key.4 = ".concat(keyToString(keyUsedAs4)));
        printer.println("key.5 = ".concat(keyToString(keyUsedAs5)));
        printer.println("key.6 = ".concat(keyToString(keyUsedAs6)));
        printer.println("key.7 = ".concat(keyToString(keyUsedAs7)));
        printer.println("key.8 = ".concat(keyToString(keyUsedAs8)));
        printer.println("key.9 = ".concat(keyToString(keyUsedAs9)));
        printer.println("key.ASTERISK = ".concat(keyToString(keyUsedAsStar)));
        printer.println("key.0 = ".concat(keyToString(keyUsedAs0)));
        printer.println("key.POUND = ".concat(keyToString(keyUsedAsPound)));
        printer.println("key.UP = ".concat(keyToString(keyUsedAsUp)));
        printer.println("key.DOWN = ".concat(keyToString(keyUsedAsDown)));
        printer.println("key.LEFT = ".concat(keyToString(keyUsedAsLeft)));
        printer.println("key.RIGHT = ".concat(keyToString(keyUsedAsRight)));
        printer.println("key.SELECT = ".concat(keyToString(keyUsedAsSelect)));
        printer.println("key.SOFT1 = ".concat(keyToString(keyUsedAsSoft1)));
        printer.println("key.SOFT2 = ".concat(keyToString(keyUsedAsSoft2)));
        printer.println("key.CONSOLE = ".concat(keyToString(keyUsedAsConsole)));
        printer.println("key.EXITAPP = ".concat(keyToString(keyUsedAsExitApp)));
        printer.println();
        printer.println("key.code.UP = ".concat(Integer.toString(keyCodeForUp)));
        printer.println("key.code.DOWN = ".concat(Integer.toString(keyCodeForDown)));
        printer.println("key.code.LEFT = ".concat(Integer.toString(keyCodeForLeft)));
        printer.println("key.code.RIGHT = ".concat(Integer.toString(keyCodeForRight)));
        printer.println("key.code.SELECT = ".concat(Integer.toString(keyCodeForSelect)));
        printer.println("key.code.SOFT1 = ".concat(Integer.toString(keyCodeForSoft1)));
        printer.println("key.code.SOFT2 = ".concat(Integer.toString(keyCodeForSoft2)));
        printer.println();
        {
            SystemFont[] f = fonts;
            SystemFont def = SystemFont.getDefault();
            StringBuilder b = new StringBuilder();
            for(int len = f.length, i = 0; i < len; b.clear(), i++)
            {
                SystemFont cur = f[i];
                printer.println(b.append(FONT_KEYS[i]).append(" = ").append((cur == null ? def : cur).getName()).toString());
            }
        }
        printer.println();
        printer.println("# Далее идут настройки, определённые пользователем");
        printer.println();
        writeMIDletProperties(printer);
    }
}
