diff --git a/.gbddcmd b/.gbddcmd new file mode 100644 index 000000000..3af237ba6 --- /dev/null +++ b/.gbddcmd @@ -0,0 +1,2387 @@ +python + +# GDB dashboard - Modular visual interface for GDB in Python. +# +# https://github.com/cyrus-and/gdb-dashboard + +# License ---------------------------------------------------------------------- + +# Copyright (c) 2015-2025 Andrea Cardaci +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Imports ---------------------------------------------------------------------- + +import ast +import io +import itertools +import math +import os +import re +import struct +import traceback + +# Common attributes ------------------------------------------------------------ + +class R(): + + @staticmethod + def attributes(): + return { + # miscellaneous + 'ansi': { + 'doc': 'Control the ANSI output of the dashboard.', + 'default': True, + 'type': bool + }, + 'syntax_highlighting': { + 'doc': '''Pygments style to use for syntax highlighting. + +Using an empty string (or a name not in the list) disables this feature. The +list of all the available styles can be obtained with (from GDB itself): + + python from pygments.styles import * + python for style in get_all_styles(): print(style)''', + 'default': 'monokai' + }, + 'discard_scrollback': { + 'doc': '''Discard the scrollback buffer at each redraw. + +This makes scrolling less confusing by discarding the previously printed +dashboards but only works with certain terminals.''', + 'default': True, + 'type': bool + }, + # values formatting + 'compact_values': { + 'doc': 'Display complex objects in a single line.', + 'default': True, + 'type': bool + }, + 'max_value_length': { + 'doc': 'Maximum length of displayed values before truncation.', + 'default': 100, + 'type': int + }, + 'value_truncation_string': { + 'doc': 'String to use to mark value truncation.', + 'default': '…', + }, + 'dereference': { + 'doc': 'Annotate pointers with the pointed value.', + 'default': True, + 'type': bool + }, + # prompt + 'prompt': { + 'doc': '''GDB prompt. + +This value is used as a Python format string where `{status}` is expanded with +the substitution of either `prompt_running` or `prompt_not_running` attributes, +according to the target program status. The resulting string must be a valid GDB +prompt, see the command `python print(gdb.prompt.prompt_help())`''', + 'default': '{status}' + }, + 'prompt_running': { + 'doc': '''Define the value of `{status}` when the target program is running. + +See the `prompt` attribute. This value is used as a Python format string where +`{pid}` is expanded with the process identifier of the target program.''', + 'default': r'\[\e[1;35m\]>>>\[\e[0m\]' + }, + 'prompt_not_running': { + 'doc': '''Define the value of `{status}` when the target program is running. + +See the `prompt` attribute. This value is used as a Python format string.''', + 'default': r'\[\e[90m\]>>>\[\e[0m\]' + }, + # divider + 'omit_divider': { + 'doc': 'Omit the divider in external outputs when only one module is displayed.', + 'default': False, + 'type': bool + }, + 'divider_fill_char_primary': { + 'doc': 'Filler around the label for primary dividers', + 'default': '─' + }, + 'divider_fill_char_secondary': { + 'doc': 'Filler around the label for secondary dividers', + 'default': '─' + }, + 'divider_fill_style_primary': { + 'doc': 'Style for `divider_fill_char_primary`', + 'default': '36' + }, + 'divider_fill_style_secondary': { + 'doc': 'Style for `divider_fill_char_secondary`', + 'default': '90' + }, + 'divider_label_style_on_primary': { + 'doc': 'Label style for non-empty primary dividers', + 'default': '1;33' + }, + 'divider_label_style_on_secondary': { + 'doc': 'Label style for non-empty secondary dividers', + 'default': '1;37' + }, + 'divider_label_style_off_primary': { + 'doc': 'Label style for empty primary dividers', + 'default': '33' + }, + 'divider_label_style_off_secondary': { + 'doc': 'Label style for empty secondary dividers', + 'default': '90' + }, + 'divider_label_skip': { + 'doc': 'Gap between the aligning border and the label.', + 'default': 3, + 'type': int, + 'check': check_ge_zero + }, + 'divider_label_margin': { + 'doc': 'Number of spaces around the label.', + 'default': 1, + 'type': int, + 'check': check_ge_zero + }, + 'divider_label_align_right': { + 'doc': 'Label alignment flag.', + 'default': False, + 'type': bool + }, + # common styles + 'style_selected_1': { + 'default': '1;32' + }, + 'style_selected_2': { + 'default': '32' + }, + 'style_low': { + 'default': '90' + }, + 'style_high': { + 'default': '1;37' + }, + 'style_error': { + 'default': '31' + }, + 'style_critical': { + 'default': '0;41' + } + } + +# Common ----------------------------------------------------------------------- + +class Beautifier(): + + def __init__(self, hint, tab_size=4): + self.tab_spaces = ' ' * tab_size if tab_size else None + self.active = False + if not R.ansi or not R.syntax_highlighting: + return + # attempt to set up Pygments + try: + import pygments + from pygments.lexers import GasLexer, NasmLexer + from pygments.formatters import Terminal256Formatter + if hint == 'att': + self.lexer = GasLexer() + elif hint == 'intel': + self.lexer = NasmLexer() + else: + from pygments.lexers import get_lexer_for_filename + self.lexer = get_lexer_for_filename(hint, stripnl=False) + self.formatter = Terminal256Formatter(style=R.syntax_highlighting) + self.active = True + except ImportError: + # Pygments not available + pass + except pygments.util.ClassNotFound: + # no lexer for this file or invalid style + pass + + def process(self, source): + # convert tabs if requested + if self.tab_spaces: + source = source.replace('\t', self.tab_spaces) + if self.active: + import pygments + source = pygments.highlight(source, self.lexer, self.formatter) + return source.rstrip('\n') + +def run(command): + return gdb.execute(command, to_string=True) + +def ansi(string, style): + if R.ansi: + return '\x1b[{}m{}\x1b[0m'.format(style, string) + else: + return string + +def divider(width, label='', primary=False, active=True): + if primary: + divider_fill_style = R.divider_fill_style_primary + divider_fill_char = R.divider_fill_char_primary + divider_label_style_on = R.divider_label_style_on_primary + divider_label_style_off = R.divider_label_style_off_primary + else: + divider_fill_style = R.divider_fill_style_secondary + divider_fill_char = R.divider_fill_char_secondary + divider_label_style_on = R.divider_label_style_on_secondary + divider_label_style_off = R.divider_label_style_off_secondary + if label: + if active: + divider_label_style = divider_label_style_on + else: + divider_label_style = divider_label_style_off + skip = R.divider_label_skip + margin = R.divider_label_margin + before = ansi(divider_fill_char * skip, divider_fill_style) + middle = ansi(label, divider_label_style) + after_length = width - len(label) - skip - 2 * margin + after = ansi(divider_fill_char * after_length, divider_fill_style) + if R.divider_label_align_right: + before, after = after, before + return ''.join([before, ' ' * margin, middle, ' ' * margin, after]) + else: + return ansi(divider_fill_char * width, divider_fill_style) + +def check_gt_zero(x): + return x > 0 + +def check_ge_zero(x): + return x >= 0 + +def to_unsigned(value, size=8): + # values from GDB can be used transparently but are not suitable for + # being printed as unsigned integers, so a conversion is needed + mask = (2 ** (size * 8)) - 1 + return int(value.cast(gdb.Value(mask).type)) & mask + +def to_string(value): + # attempt to convert an inferior value to string; OK when (Python 3 || + # simple ASCII); otherwise (Python 2.7 && not ASCII) encode the string as + # utf8 + try: + value_string = str(value) + except UnicodeEncodeError: + value_string = unicode(value).encode('utf8') + except gdb.error as e: + value_string = ansi(e, R.style_error) + return value_string + +def format_address(address): + pointer_size = gdb.parse_and_eval('$pc').type.sizeof + return ('0x{{:0{}x}}').format(pointer_size * 2).format(address) + +def format_value(value, compact=None): + # format references as referenced values + # (TYPE_CODE_RVALUE_REF is not supported by old GDB) + if value.type.code in (getattr(gdb, 'TYPE_CODE_REF', None), + getattr(gdb, 'TYPE_CODE_RVALUE_REF', None)): + try: + value = value.referenced_value() + except gdb.error as e: + return ansi(e, R.style_error) + # format the value + out = to_string(value) + # dereference up to the actual value if requested + if R.dereference and value.type.code == gdb.TYPE_CODE_PTR: + while value.type.code == gdb.TYPE_CODE_PTR: + try: + value = value.dereference() + except gdb.error as e: + break + else: + formatted = to_string(value) + out += '{} {}'.format(ansi(':', R.style_low), formatted) + # compact the value + if compact is not None and compact or R.compact_values: + out = re.sub(r'$\s*', '', out, flags=re.MULTILINE) + # truncate the value + if R.max_value_length > 0 and len(out) > R.max_value_length: + out = out[0:R.max_value_length] + ansi(R.value_truncation_string, R.style_critical) + return out + +# XXX parsing the output of `info breakpoints` is apparently the best option +# right now, see: https://sourceware.org/bugzilla/show_bug.cgi?id=18385 +# XXX GDB version 7.11 (quire recent) does not have the pending field, so +# fall back to the parsed information +def fetch_breakpoints(watchpoints=False, pending=False): + # fetch breakpoints addresses + parsed_breakpoints = dict() + catch_what_regex = re.compile(r'([^,]+".*")?[^,]*') + for line in run('info breakpoints').split('\n'): + # just keep numbered lines + if not line or not line[0].isdigit(): + continue + # extract breakpoint number, address and pending status + fields = line.split() + number = int(fields[0].split('.')[0]) + try: + if len(fields) >= 5 and fields[1] == 'breakpoint': + # multiple breakpoints have no address yet + is_pending = fields[4] == '' + is_multiple = fields[4] == '' + address = None if is_multiple or is_pending else int(fields[4], 16) + is_enabled = fields[3] == 'y' + address_info = address, is_enabled + parsed_breakpoints[number] = [address_info], is_pending, '' + elif len(fields) >= 5 and fields[1] == 'catchpoint': + # only take before comma, but ignore commas in quotes + what = catch_what_regex.search(' '.join(fields[4:])).group(0).strip() + parsed_breakpoints[number] = [], False, what + elif len(fields) >= 3 and number in parsed_breakpoints: + # add this address to the list of multiple locations + address = int(fields[2], 16) + is_enabled = fields[1] == 'y' + address_info = address, is_enabled + parsed_breakpoints[number][0].append(address_info) + else: + # watchpoints + parsed_breakpoints[number] = [], False, '' + except ValueError: + pass + # fetch breakpoints from the API and complement with address and source + # information + breakpoints = [] + # XXX in older versions gdb.breakpoints() returns None + for gdb_breakpoint in gdb.breakpoints() or []: + # skip internal breakpoints + if gdb_breakpoint.number < 0: + continue + addresses, is_pending, what = parsed_breakpoints[gdb_breakpoint.number] + is_pending = getattr(gdb_breakpoint, 'pending', is_pending) + if not pending and is_pending: + continue + if not watchpoints and gdb_breakpoint.type != gdb.BP_BREAKPOINT: + continue + # add useful fields to the object + breakpoint = dict() + breakpoint['number'] = gdb_breakpoint.number + breakpoint['type'] = gdb_breakpoint.type + breakpoint['enabled'] = gdb_breakpoint.enabled + breakpoint['location'] = gdb_breakpoint.location + breakpoint['expression'] = gdb_breakpoint.expression + breakpoint['condition'] = gdb_breakpoint.condition + breakpoint['temporary'] = gdb_breakpoint.temporary + breakpoint['hit_count'] = gdb_breakpoint.hit_count + breakpoint['pending'] = is_pending + breakpoint['what'] = what + # add addresses and source information + breakpoint['addresses'] = [] + for address, is_enabled in addresses: + if address: + sal = gdb.find_pc_line(address) + breakpoint['addresses'].append({ + 'address': address, + 'enabled': is_enabled, + 'file_name': sal.symtab.filename if address and sal.symtab else None, + 'file_line': sal.line if address else None + }) + breakpoints.append(breakpoint) + return breakpoints + +# Dashboard -------------------------------------------------------------------- + +class Dashboard(gdb.Command): + '''Redisplay the dashboard.''' + + def __init__(self): + gdb.Command.__init__(self, 'dashboard', gdb.COMMAND_USER, gdb.COMPLETE_NONE, True) + # setup subcommands + Dashboard.ConfigurationCommand(self) + Dashboard.OutputCommand(self) + Dashboard.EnabledCommand(self) + Dashboard.LayoutCommand(self) + # setup style commands + Dashboard.StyleCommand(self, 'dashboard', R, R.attributes()) + # main terminal + self.output = None + # used to inhibit redisplays during init parsing + self.inhibited = None + # enabled by default + self.enabled = None + self.enable() + + def on_continue(self, _): + # try to contain the GDB messages in a specified area unless the + # dashboard is printed to a separate file (dashboard -output ...) + # or there are no modules to display in the main terminal + enabled_modules = list(filter(lambda m: not m.output and m.enabled, self.modules)) + if self.is_running() and not self.output and len(enabled_modules) > 0: + width, _ = Dashboard.get_term_size() + gdb.write(Dashboard.clear_screen()) + gdb.write(divider(width, 'Output/messages', True)) + gdb.write('\n') + gdb.flush() + + def on_stop(self, _): + if self.is_running(): + self.render(clear_screen=False) + + def on_exit(self, _): + if not self.is_running(): + return + # collect all the outputs + outputs = set() + outputs.add(self.output) + outputs.update(module.output for module in self.modules) + outputs.remove(None) + # reset the terminal status + for output in outputs: + try: + with open(output, 'w') as fs: + fs.write(Dashboard.reset_terminal()) + except: + # skip cleanup for invalid outputs + pass + + def enable(self): + if self.enabled: + return + self.enabled = True + # setup events + gdb.events.cont.connect(self.on_continue) + gdb.events.stop.connect(self.on_stop) + gdb.events.exited.connect(self.on_exit) + + def disable(self): + if not self.enabled: + return + self.enabled = False + # setup events + gdb.events.cont.disconnect(self.on_continue) + gdb.events.stop.disconnect(self.on_stop) + gdb.events.exited.disconnect(self.on_exit) + + def load_modules(self, modules): + self.modules = [] + for module in modules: + info = Dashboard.ModuleInfo(self, module) + self.modules.append(info) + + def redisplay(self, style_changed=False): + # manually redisplay the dashboard + if self.is_running() and not self.inhibited: + self.render(True, style_changed) + + def inferior_pid(self): + return gdb.selected_inferior().pid + + def is_running(self): + return self.inferior_pid() != 0 + + def render(self, clear_screen, style_changed=False): + # fetch module content and info + all_disabled = True + display_map = dict() + for module in self.modules: + # fall back to the global value + output = module.output or self.output + # add the instance or None if disabled + if module.enabled: + all_disabled = False + instance = module.instance + else: + instance = None + display_map.setdefault(output, []).append(instance) + # process each display info + for output, instances in display_map.items(): + try: + buf = '' + # use GDB stream by default + fs = None + if output: + fs = open(output, 'w') + fd = fs.fileno() + fs.write(Dashboard.setup_terminal()) + else: + fs = gdb + fd = 1 # stdout + # get the terminal size (default main terminal if either the + # output is not a file) + try: + width, height = Dashboard.get_term_size(fd) + except: + width, height = Dashboard.get_term_size() + # clear the "screen" if requested for the main terminal, + # auxiliary terminals are always cleared + if fs is not gdb or clear_screen: + buf += Dashboard.clear_screen() + # show message if all the modules in this output are disabled + if not any(instances): + # skip the main terminal + if fs is gdb: + continue + # write the error message + buf += divider(width, 'Warning', True) + buf += '\n' + if self.modules: + buf += 'No module to display (see `dashboard -layout`)' + else: + buf += 'No module loaded' + buf += '\n' + fs.write(buf) + continue + # process all the modules for that output + for n, instance in enumerate(instances, 1): + # skip disabled modules + if not instance: + continue + try: + # ask the module to generate the content + lines = instance.lines(width, height, style_changed) + except Exception as e: + # allow to continue on exceptions in modules + stacktrace = traceback.format_exc().strip() + lines = [ansi(stacktrace, R.style_error)] + # create the divider if needed + div = [] + if not R.omit_divider or len(instances) > 1 or fs is gdb: + div = [divider(width, instance.label(), True, lines)] + # write the data + buf += '\n'.join(div + lines) + # write the newline for all but last unless main terminal + if n != len(instances) or fs is gdb: + buf += '\n' + # write the final newline and the terminator only if it is the + # main terminal to allow the prompt to display correctly (unless + # there are no modules to display) + if fs is gdb and not all_disabled: + buf += divider(width, primary=True) + buf += '\n' + fs.write(buf) + except Exception as e: + cause = traceback.format_exc().strip() + Dashboard.err('Cannot write the dashboard\n{}'.format(cause)) + finally: + # don't close gdb stream + if fs and fs is not gdb: + fs.close() + +# Utility methods -------------------------------------------------------------- + + @staticmethod + def start(): + # save the instance for customization convenience + global dashboard + # initialize the dashboard + dashboard = Dashboard() + Dashboard.set_custom_prompt(dashboard) + # parse Python inits, load modules then parse GDB inits + dashboard.inhibited = True + Dashboard.parse_inits(True) + modules = Dashboard.get_modules() + dashboard.load_modules(modules) + Dashboard.parse_inits(False) + dashboard.inhibited = False + # GDB overrides + run('set pagination off') + # display if possible (program running and not explicitly disabled by + # some configuration file) + if dashboard.enabled: + dashboard.redisplay() + + @staticmethod + def get_term_size(fd=1): # defaults to the main terminal + try: + if sys.platform == 'win32': + import curses + # XXX always neglects the fd parameter + height, width = curses.initscr().getmaxyx() + curses.endwin() + return int(width), int(height) + else: + import termios + import fcntl + # first 2 shorts (4 byte) of struct winsize + raw = fcntl.ioctl(fd, termios.TIOCGWINSZ, ' ' * 4) + height, width = struct.unpack('hh', raw) + return int(width), int(height) + except (ImportError, OSError): + # this happens when no curses library is found on windows or when + # the terminal is not properly configured + return 80, 24 # hardcoded fallback value + + @staticmethod + def set_custom_prompt(dashboard): + def custom_prompt(_): + # render thread status indicator + if dashboard.is_running(): + pid = dashboard.inferior_pid() + status = R.prompt_running.format(pid=pid) + else: + status = R.prompt_not_running + # build prompt + prompt = R.prompt.format(status=status) + prompt = gdb.prompt.substitute_prompt(prompt) + return prompt + ' ' # force trailing space + gdb.prompt_hook = custom_prompt + + @staticmethod + def parse_inits(python): + # paths where the .gdbinit.d directory might be + search_paths = [ + '/etc/gdb-dashboard', + '{}/gdb-dashboard'.format(os.getenv('XDG_CONFIG_HOME', '~/.config')), + '~/Library/Preferences/gdb-dashboard', + '~/.gdbinit.d' + ] + # expand the tilde and walk the paths + inits_dirs = (os.walk(os.path.expanduser(path)) for path in search_paths) + # process all the init files in order + for root, dirs, files in itertools.chain.from_iterable(inits_dirs): + dirs.sort() + # skipping dotfiles + for init in sorted(file for file in files if not file.startswith('.')): + path = os.path.join(root, init) + _, ext = os.path.splitext(path) + # either load Python files or GDB + if python == (ext == '.py'): + gdb.execute('source ' + path) + + @staticmethod + def get_modules(): + # scan the scope for modules + modules = [] + for name in globals(): + obj = globals()[name] + try: + if issubclass(obj, Dashboard.Module): + modules.append(obj) + except TypeError: + continue + # sort modules alphabetically + modules.sort(key=lambda x: x.__name__) + return modules + + @staticmethod + def create_command(name, invoke, doc, is_prefix, complete=None): + if callable(complete): + Class = type('', (gdb.Command,), { + '__doc__': doc, + 'invoke': invoke, + 'complete': complete + }) + Class(name, gdb.COMMAND_USER, prefix=is_prefix) + else: + Class = type('', (gdb.Command,), { + '__doc__': doc, + 'invoke': invoke + }) + Class(name, gdb.COMMAND_USER, complete or gdb.COMPLETE_NONE, is_prefix) + + @staticmethod + def err(string): + print(ansi(string, R.style_error)) + + @staticmethod + def complete(word, candidates): + return filter(lambda candidate: candidate.startswith(word), candidates) + + @staticmethod + def parse_arg(arg): + # encode unicode GDB command arguments as utf8 in Python 2.7 + if type(arg) is not str: + arg = arg.encode('utf8') + return arg + + @staticmethod + def clear_screen(): + # ANSI: move the cursor to top-left corner and clear the screen + # (optionally also clear the scrollback buffer if supported by the + # terminal) + return '\x1b[H\x1b[2J' + ('\x1b[3J' if R.discard_scrollback else '') + + @staticmethod + def setup_terminal(): + # ANSI: enable alternative screen buffer and hide cursor + return '\x1b[?1049h\x1b[?25l' + + @staticmethod + def reset_terminal(): + # ANSI: disable alternative screen buffer and show cursor + return '\x1b[?1049l\x1b[?25h' + +# Module descriptor ------------------------------------------------------------ + + class ModuleInfo: + + def __init__(self, dashboard, module): + self.name = module.__name__.lower() # from class to module name + self.enabled = True + self.output = None # value from the dashboard by default + self.instance = module() + self.doc = self.instance.__doc__ or '(no documentation)' + self.prefix = 'dashboard {}'.format(self.name) + # add GDB commands + self.add_main_command(dashboard) + self.add_output_command(dashboard) + self.add_style_command(dashboard) + self.add_subcommands(dashboard) + + def add_main_command(self, dashboard): + module = self + def invoke(self, arg, from_tty, info=self): + arg = Dashboard.parse_arg(arg) + if arg == '': + info.enabled ^= True + if dashboard.is_running(): + dashboard.redisplay() + else: + status = 'enabled' if info.enabled else 'disabled' + print('{} module {}'.format(module.name, status)) + else: + Dashboard.err('Wrong argument "{}"'.format(arg)) + doc_brief = 'Configure the {} module, with no arguments toggles its visibility.'.format(self.name) + doc = '{}\n\n{}'.format(doc_brief, self.doc) + Dashboard.create_command(self.prefix, invoke, doc, True) + + def add_output_command(self, dashboard): + Dashboard.OutputCommand(dashboard, self.prefix, self) + + def add_style_command(self, dashboard): + Dashboard.StyleCommand(dashboard, self.prefix, self.instance, self.instance.attributes()) + + def add_subcommands(self, dashboard): + for name, command in self.instance.commands().items(): + self.add_subcommand(dashboard, name, command) + + def add_subcommand(self, dashboard, name, command): + action = command['action'] + doc = command['doc'] + complete = command.get('complete') + def invoke(self, arg, from_tty, info=self): + arg = Dashboard.parse_arg(arg) + if info.enabled: + try: + action(arg) + except Exception as e: + Dashboard.err(e) + return + # don't catch redisplay errors + dashboard.redisplay() + else: + Dashboard.err('Module disabled') + prefix = '{} {}'.format(self.prefix, name) + Dashboard.create_command(prefix, invoke, doc, False, complete) + +# GDB commands ----------------------------------------------------------------- + + # handler for the `dashboard` command itself + def invoke(self, arg, from_tty): + arg = Dashboard.parse_arg(arg) + # show messages for checks in redisplay + if arg != '': + Dashboard.err('Wrong argument "{}"'.format(arg)) + elif not self.is_running(): + Dashboard.err('Is the target program running?') + else: + self.redisplay() + + class ConfigurationCommand(gdb.Command): + '''Dump or save the dashboard configuration. + +With an optional argument the configuration will be written to the specified +file. + +This command allows to configure the dashboard live then make the changes +permanent, for example: + + dashboard -configuration ~/.gdbinit.d/init + +At startup the `~/.gdbinit.d/` directory tree is walked and files are evaluated +in alphabetical order but giving priority to Python files. This is where user +configuration files must be placed.''' + + def __init__(self, dashboard): + gdb.Command.__init__(self, 'dashboard -configuration', + gdb.COMMAND_USER, gdb.COMPLETE_FILENAME) + self.dashboard = dashboard + + def invoke(self, arg, from_tty): + arg = Dashboard.parse_arg(arg) + if arg: + with open(os.path.expanduser(arg), 'w') as fs: + fs.write('# auto generated by GDB dashboard\n\n') + self.dump(fs) + self.dump(gdb) + + def dump(self, fs): + # dump layout + self.dump_layout(fs) + # dump styles + self.dump_style(fs, R) + for module in self.dashboard.modules: + self.dump_style(fs, module.instance, module.prefix) + # dump outputs + self.dump_output(fs, self.dashboard) + for module in self.dashboard.modules: + self.dump_output(fs, module, module.prefix) + + def dump_layout(self, fs): + layout = ['dashboard -layout'] + for module in self.dashboard.modules: + mark = '' if module.enabled else '!' + layout.append('{}{}'.format(mark, module.name)) + fs.write(' '.join(layout)) + fs.write('\n') + + def dump_style(self, fs, obj, prefix='dashboard'): + attributes = getattr(obj, 'attributes', lambda: dict())() + for name, attribute in attributes.items(): + real_name = attribute.get('name', name) + default = attribute.get('default') + value = getattr(obj, real_name) + if value != default: + fs.write('{} -style {} {!r}\n'.format(prefix, name, value)) + + def dump_output(self, fs, obj, prefix='dashboard'): + output = getattr(obj, 'output') + if output: + fs.write('{} -output {}\n'.format(prefix, output)) + + class OutputCommand(gdb.Command): + '''Set the output file/TTY for the whole dashboard or single modules. + +The dashboard/module will be written to the specified file, which will be +created if it does not exist. If the specified file identifies a terminal then +its geometry will be used, otherwise it falls back to the geometry of the main +GDB terminal. + +When invoked without argument on the dashboard, the output/messages and modules +which do not specify an output themselves will be printed on standard output +(default). + +When invoked without argument on a module, it will be printed where the +dashboard will be printed. + +An overview of all the outputs can be obtained with the `dashboard -layout` +command.''' + + def __init__(self, dashboard, prefix=None, obj=None): + if not prefix: + prefix = 'dashboard' + if not obj: + obj = dashboard + prefix = prefix + ' -output' + gdb.Command.__init__(self, prefix, gdb.COMMAND_USER, gdb.COMPLETE_FILENAME) + self.dashboard = dashboard + self.obj = obj # None means the dashboard itself + + def invoke(self, arg, from_tty): + arg = Dashboard.parse_arg(arg) + # reset the terminal status + if self.obj.output: + try: + with open(self.obj.output, 'w') as fs: + fs.write(Dashboard.reset_terminal()) + except: + # just do nothing if the file is not writable + pass + # set or open the output file + if arg == '': + self.obj.output = None + else: + self.obj.output = arg + # redisplay the dashboard in the new output + self.dashboard.redisplay() + + class EnabledCommand(gdb.Command): + '''Enable or disable the dashboard. + +The current status is printed if no argument is present.''' + + def __init__(self, dashboard): + gdb.Command.__init__(self, 'dashboard -enabled', gdb.COMMAND_USER) + self.dashboard = dashboard + + def invoke(self, arg, from_tty): + arg = Dashboard.parse_arg(arg) + if arg == '': + status = 'enabled' if self.dashboard.enabled else 'disabled' + print('The dashboard is {}'.format(status)) + elif arg == 'on': + self.dashboard.enable() + self.dashboard.redisplay() + elif arg == 'off': + self.dashboard.disable() + else: + msg = 'Wrong argument "{}"; expecting "on" or "off"' + Dashboard.err(msg.format(arg)) + + def complete(self, text, word): + return Dashboard.complete(word, ['on', 'off']) + + class LayoutCommand(gdb.Command): + '''Set or show the dashboard layout. + +Accepts a space-separated list of directive. Each directive is in the form +"[!]". Modules in the list are placed in the dashboard in the same order +as they appear and those prefixed by "!" are disabled by default. Omitted +modules are hidden and placed at the bottom in alphabetical order. + +Without arguments the current layout is shown where the first line uses the same +form expected by the input while the remaining depict the current status of +output files. + +Passing `!` as a single argument resets the dashboard original layout.''' + + def __init__(self, dashboard): + gdb.Command.__init__(self, 'dashboard -layout', gdb.COMMAND_USER) + self.dashboard = dashboard + + def invoke(self, arg, from_tty): + arg = Dashboard.parse_arg(arg) + directives = str(arg).split() + if directives: + # apply the layout + if directives == ['!']: + self.reset() + else: + if not self.layout(directives): + return # in case of errors + # redisplay or otherwise notify + if from_tty: + if self.dashboard.is_running(): + self.dashboard.redisplay() + else: + self.show() + else: + self.show() + + def reset(self): + modules = self.dashboard.modules + modules.sort(key=lambda module: module.name) + for module in modules: + module.enabled = True + + def show(self): + global_str = 'Dashboard' + default = '(default TTY)' + max_name_len = max(len(module.name) for module in self.dashboard.modules) + max_name_len = max(max_name_len, len(global_str)) + fmt = '{{}}{{:{}s}}{{}}'.format(max_name_len + 2) + print((fmt + '\n').format(' ', global_str, self.dashboard.output or default)) + for module in self.dashboard.modules: + mark = ' ' if module.enabled else '!' + style = R.style_high if module.enabled else R.style_low + line = fmt.format(mark, module.name, module.output or default) + print(ansi(line, style)) + + def layout(self, directives): + modules = self.dashboard.modules + # parse and check directives + parsed_directives = [] + selected_modules = set() + for directive in directives: + enabled = (directive[0] != '!') + name = directive[not enabled:] + if name in selected_modules: + Dashboard.err('Module "{}" already set'.format(name)) + return False + if next((False for module in modules if module.name == name), True): + Dashboard.err('Cannot find module "{}"'.format(name)) + return False + parsed_directives.append((name, enabled)) + selected_modules.add(name) + # reset visibility + for module in modules: + module.enabled = False + # move and enable the selected modules on top + last = 0 + for name, enabled in parsed_directives: + todo = enumerate(modules[last:], start=last) + index = next(index for index, module in todo if name == module.name) + modules[index].enabled = enabled + modules.insert(last, modules.pop(index)) + last += 1 + return True + + def complete(self, text, word): + all_modules = (m.name for m in self.dashboard.modules) + return Dashboard.complete(word, all_modules) + + class StyleCommand(gdb.Command): + '''Access the stylable attributes. + +Without arguments print all the stylable attributes. + +When only the name is specified show the current value. + +With name and value set the stylable attribute. Values are parsed as Python +literals and converted to the proper type. ''' + + def __init__(self, dashboard, prefix, obj, attributes): + self.prefix = prefix + ' -style' + gdb.Command.__init__(self, self.prefix, gdb.COMMAND_USER, gdb.COMPLETE_NONE, True) + self.dashboard = dashboard + self.obj = obj + self.attributes = attributes + self.add_styles() + + def add_styles(self): + this = self + for name, attribute in self.attributes.items(): + # fetch fields + attr_name = attribute.get('name', name) + attr_type = attribute.get('type', str) + attr_check = attribute.get('check', lambda _: True) + attr_default = attribute['default'] + # set the default value (coerced to the type) + value = attr_type(attr_default) + setattr(self.obj, attr_name, value) + # create the command + def invoke(self, arg, from_tty, + name=name, + attr_name=attr_name, + attr_type=attr_type, + attr_check=attr_check): + new_value = Dashboard.parse_arg(arg) + if new_value == '': + # print the current value + value = getattr(this.obj, attr_name) + print('{} = {!r}'.format(name, value)) + else: + try: + # convert and check the new value + parsed = ast.literal_eval(new_value) + value = attr_type(parsed) + if not attr_check(value): + msg = 'Invalid value "{}" for "{}"' + raise Exception(msg.format(new_value, name)) + except Exception as e: + Dashboard.err(e) + else: + # set and redisplay + setattr(this.obj, attr_name, value) + this.dashboard.redisplay(True) + prefix = self.prefix + ' ' + name + doc = attribute.get('doc', 'This style is self-documenting') + Dashboard.create_command(prefix, invoke, doc, False) + + def invoke(self, arg, from_tty): + # an argument here means that the provided attribute is invalid + if arg: + Dashboard.err('Invalid argument "{}"'.format(arg)) + return + # print all the pairs + for name, attribute in self.attributes.items(): + attr_name = attribute.get('name', name) + value = getattr(self.obj, attr_name) + print('{} = {!r}'.format(name, value)) + +# Base module ------------------------------------------------------------------ + + # just a tag + class Module(): + '''Base class for GDB dashboard modules. + + Modules are instantiated once at initialization time and kept during the + whole the GDB session. + + The name of a module is automatically obtained by the class name. + + Optionally, a module may include a description which will appear in the + GDB help system by specifying a Python docstring for the class. By + convention the first line should contain a brief description.''' + + def label(self): + '''Return the module label which will appear in the divider.''' + pass + + def lines(self, term_width, term_height, style_changed): + '''Return a list of strings which will form the module content. + + When a module is temporarily unable to produce its content, it + should return an empty list; its divider will then use the styles + with the "off" qualifier. + + term_width and term_height are the dimension of the terminal where + this module will be displayed. If `style_changed` is `True` then + some attributes have changed since the last time so the + implementation may want to update its status.''' + pass + + def attributes(self): + '''Return the dictionary of available attributes. + + The key is the attribute name and the value is another dictionary + with items: + + - `default` is the initial value for this attribute; + + - `doc` is the optional documentation of this attribute which will + appear in the GDB help system; + + - `name` is the name of the attribute of the Python object (defaults + to the key value); + + - `type` is the Python type of this attribute defaulting to the + `str` type, it is used to coerce the value passed as an argument + to the proper type, or raise an exception; + + - `check` is an optional control callback which accept the coerced + value and returns `True` if the value satisfies the constraint and + `False` otherwise. + + Those attributes can be accessed from the implementation using + instance variables named `name`.''' + return {} + + def commands(self): + '''Return the dictionary of available commands. + + The key is the attribute name and the value is another dictionary + with items: + + - `action` is the callback to be executed which accepts the raw + input string from the GDB prompt, exceptions in these functions + will be shown automatically to the user; + + - `doc` is the documentation of this command which will appear in + the GDB help system; + + - `completion` is the optional completion policy, one of the + `gdb.COMPLETE_*` constants defined in the GDB reference manual + (https://sourceware.org/gdb/onlinedocs/gdb/Commands-In-Python.html).''' + return {} + +# Default modules -------------------------------------------------------------- + +class Source(Dashboard.Module): + '''Show the program source code, if available.''' + + def __init__(self): + self.file_name = None + self.source_lines = [] + self.ts = None + self.highlighted = False + self.offset = 0 + + def label(self): + label = 'Source' + if self.show_path and self.file_name: + label += ': {}'.format(self.file_name) + return label + + def lines(self, term_width, term_height, style_changed): + # skip if the current thread is not stopped + if not gdb.selected_thread().is_stopped(): + return [] + # try to fetch the current line (skip if no line information) + sal = gdb.selected_frame().find_sal() + current_line = sal.line + if current_line == 0: + self.file_name = None + return [] + # try to lookup the source file + candidates = [ + sal.symtab.fullname(), + sal.symtab.filename, + # XXX GDB also uses absolute filename but it is harder to implement + # properly and IMHO useless + os.path.basename(sal.symtab.filename)] + for candidate in candidates: + file_name = candidate + ts = None + try: + ts = os.path.getmtime(file_name) + break + except: + # try another or delay error check to open() + continue + # style changed, different file name or file modified in the meanwhile + if style_changed or file_name != self.file_name or ts and ts > self.ts: + try: + # reload the source file if changed + with io.open(file_name, errors='replace') as source_file: + highlighter = Beautifier(file_name, self.tab_size) + self.highlighted = highlighter.active + source = highlighter.process(source_file.read()) + self.source_lines = source.split('\n') + # store file name and timestamp only if success to have + # persistent errors + self.file_name = file_name + self.ts = ts + except IOError as e: + msg = 'Cannot display "{}"'.format(file_name) + return [ansi(msg, R.style_error)] + # compute the line range + height = self.height or (term_height - 1) + start = current_line - 1 - int(height / 2) + self.offset + end = start + height + # extra at start + extra_start = 0 + if start < 0: + extra_start = min(-start, height) + start = 0 + # extra at end + extra_end = 0 + if end > len(self.source_lines): + extra_end = min(end - len(self.source_lines), height) + end = len(self.source_lines) + else: + end = max(end, 0) + # return the source code listing + breakpoints = fetch_breakpoints() + out = [] + number_format = '{{:>{}}}'.format(len(str(end))) + for number, line in enumerate(self.source_lines[start:end], start + 1): + # properly handle UTF-8 source files + line = to_string(line) + if int(number) == current_line: + # the current line has a different style without ANSI + if R.ansi: + if self.highlighted and not self.highlight_line: + line_format = '{}' + ansi(number_format, R.style_selected_1) + ' {}' + else: + line_format = '{}' + ansi(number_format + ' {}', R.style_selected_1) + else: + # just show a plain text indicator + line_format = '{}' + number_format + '> {}' + else: + line_format = '{}' + ansi(number_format, R.style_low) + ' {}' + # check for breakpoint presence + enabled = None + for breakpoint in breakpoints: + addresses = breakpoint['addresses'] + is_root_enabled = addresses[0]['enabled'] + for address in addresses: + # note, despite the lookup path always use the relative + # (sal.symtab.filename) file name to match source files with + # breakpoints + if address['file_line'] == number and address['file_name'] == sal.symtab.filename: + enabled = enabled or (address['enabled'] and is_root_enabled) + if enabled is None: + breakpoint = ' ' + else: + breakpoint = ansi('!', R.style_critical) if enabled else ansi('-', R.style_low) + out.append(line_format.format(breakpoint, number, line.rstrip('\n'))) + # return the output along with scroll indicators + if len(out) <= height: + extra = [ansi('~', R.style_low)] + return extra_start * extra + out + extra_end * extra + else: + return out + + def commands(self): + return { + 'scroll': { + 'action': self.scroll, + 'doc': 'Scroll by relative steps or reset if invoked without argument.' + } + } + + def attributes(self): + return { + 'height': { + 'doc': '''Height of the module. + +A value of 0 uses the whole height.''', + 'default': 10, + 'type': int, + 'check': check_ge_zero + }, + 'tab-size': { + 'doc': 'Number of spaces used to display the tab character.', + 'default': 4, + 'name': 'tab_size', + 'type': int, + 'check': check_gt_zero + }, + 'path': { + 'doc': 'Path visibility flag in the module label.', + 'default': False, + 'name': 'show_path', + 'type': bool + }, + 'highlight-line': { + 'doc': 'Decide whether the whole current line should be highlighted.', + 'default': False, + 'name': 'highlight_line', + 'type': bool + } + } + + def scroll(self, arg): + if arg: + self.offset += int(arg) + else: + self.offset = 0 + +class Assembly(Dashboard.Module): + '''Show the disassembled code surrounding the program counter. + +The instructions constituting the current statement are marked, if available.''' + + def __init__(self): + self.offset = 0 + self.cache_key = None + self.cache_asm = None + + def label(self): + return 'Assembly' + + def lines(self, term_width, term_height, style_changed): + # skip if the current thread is not stopped + if not gdb.selected_thread().is_stopped(): + return [] + # flush the cache if the style is changed + if style_changed: + self.cache_key = None + # prepare the highlighter + try: + flavor = gdb.parameter('disassembly-flavor') + except: + flavor = 'att' # not always defined (see #36) + highlighter = Beautifier(flavor, tab_size=None) + # fetch the assembly code + line_info = None + frame = gdb.selected_frame() # PC is here + height = self.height or (term_height - 1) + try: + # disassemble the current block + asm_start, asm_end = self.fetch_function_boundaries() + asm = self.fetch_asm(asm_start, asm_end, False, highlighter) + # find the location of the PC + pc_index = next(index for index, instr in enumerate(asm) + if instr['addr'] == frame.pc()) + # compute the instruction range + start = pc_index - int(height / 2) + self.offset + end = start + height + # extra at start + extra_start = 0 + if start < 0: + extra_start = min(-start, height) + start = 0 + # extra at end + extra_end = 0 + if end > len(asm): + extra_end = min(end - len(asm), height) + end = len(asm) + else: + end = max(end, 0) + # fetch actual interval + asm = asm[start:end] + # if there are line information then use it, it may be that + # line_info is not None but line_info.last is None + line_info = gdb.find_pc_line(frame.pc()) + line_info = line_info if line_info.last else None + except (gdb.error, RuntimeError, StopIteration): + # if it is not possible (stripped binary or the PC is not present in + # the output of `disassemble` as per issue #31) start from PC + try: + extra_start = 0 + extra_end = 0 + # allow to scroll down nevertheless + clamped_offset = min(self.offset, 0) + asm = self.fetch_asm(frame.pc(), height - clamped_offset, True, highlighter) + asm = asm[-clamped_offset:] + except gdb.error as e: + msg = '{}'.format(e) + return [ansi(msg, R.style_error)] + # fetch function start if available (e.g., not with @plt) + func_start = None + if self.show_function and frame.function(): + func_start = to_unsigned(frame.function().value()) + # compute the maximum offset size + if asm and func_start: + max_offset = max(len(str(abs(asm[0]['addr'] - func_start))), + len(str(abs(asm[-1]['addr'] - func_start)))) + # return the machine code + breakpoints = fetch_breakpoints() + max_length = max(instr['length'] for instr in asm) if asm else 0 + inferior = gdb.selected_inferior() + out = [] + for index, instr in enumerate(asm): + addr = instr['addr'] + length = instr['length'] + text = instr['asm'] + addr_str = format_address(addr) + if self.show_opcodes: + # fetch and format opcode + region = inferior.read_memory(addr, length) + opcodes = (' '.join('{:02x}'.format(ord(byte)) for byte in region)) + opcodes += (max_length - len(region)) * 3 * ' ' + ' ' + else: + opcodes = '' + # compute the offset if available + if self.show_function: + if func_start: + offset = '{:+d}'.format(addr - func_start) + offset = offset.ljust(max_offset + 1) # sign + func_info = '{}{}'.format(frame.function(), offset) + else: + func_info = '?' + else: + func_info = '' + format_string = '{}{}{}{}{}{}' + indicator = ' ' + text = ' ' + text + if addr == frame.pc(): + if not R.ansi: + indicator = '> ' + addr_str = ansi(addr_str, R.style_selected_1) + indicator = ansi(indicator, R.style_selected_1) + opcodes = ansi(opcodes, R.style_selected_1) + func_info = ansi(func_info, R.style_selected_1) + if not highlighter.active or self.highlight_line: + text = ansi(text, R.style_selected_1) + elif line_info and line_info.pc <= addr < line_info.last: + if not R.ansi: + indicator = ': ' + addr_str = ansi(addr_str, R.style_selected_2) + indicator = ansi(indicator, R.style_selected_2) + opcodes = ansi(opcodes, R.style_selected_2) + func_info = ansi(func_info, R.style_selected_2) + if not highlighter.active or self.highlight_line: + text = ansi(text, R.style_selected_2) + else: + addr_str = ansi(addr_str, R.style_low) + func_info = ansi(func_info, R.style_low) + # check for breakpoint presence + enabled = None + for breakpoint in breakpoints: + addresses = breakpoint['addresses'] + is_root_enabled = addresses[0]['enabled'] + for address in addresses: + if address['address'] == addr: + enabled = enabled or (address['enabled'] and is_root_enabled) + if enabled is None: + breakpoint = ' ' + else: + breakpoint = ansi('!', R.style_critical) if enabled else ansi('-', R.style_low) + out.append(format_string.format(breakpoint, addr_str, indicator, opcodes, func_info, text)) + # return the output along with scroll indicators + if len(out) <= height: + extra = [ansi('~', R.style_low)] + return extra_start * extra + out + extra_end * extra + else: + return out + + def commands(self): + return { + 'scroll': { + 'action': self.scroll, + 'doc': 'Scroll by relative steps or reset if invoked without argument.' + } + } + + def attributes(self): + return { + 'height': { + 'doc': '''Height of the module. + +A value of 0 uses the whole height.''', + 'default': 10, + 'type': int, + 'check': check_ge_zero + }, + 'opcodes': { + 'doc': 'Opcodes visibility flag.', + 'default': False, + 'name': 'show_opcodes', + 'type': bool + }, + 'function': { + 'doc': 'Function information visibility flag.', + 'default': True, + 'name': 'show_function', + 'type': bool + }, + 'highlight-line': { + 'doc': 'Decide whether the whole current line should be highlighted.', + 'default': False, + 'name': 'highlight_line', + 'type': bool + } + } + + def scroll(self, arg): + if arg: + self.offset += int(arg) + else: + self.offset = 0 + + def fetch_function_boundaries(self): + frame = gdb.selected_frame() + # parse the output of the disassemble GDB command to find the function + # boundaries, this should handle cases in which a function spans + # multiple discontinuous blocks + disassemble = run('disassemble') + for block_start, block_end in re.findall(r'Address range 0x([0-9a-f]+) to 0x([0-9a-f]+):', disassemble): + block_start = int(block_start, 16) + block_end = int(block_end, 16) + if block_start <= frame.pc() < block_end: + return block_start, block_end - 1 # need to be inclusive + # if function information is available then try to obtain the + # boundaries by looking at the superblocks + block = frame.block() + if frame.function(): + while block and (not block.function or block.function.name != frame.function().name): + block = block.superblock + block = block or frame.block() + return block.start, block.end - 1 + + def fetch_asm(self, start, end_or_count, relative, highlighter): + # fetch asm from cache or disassemble + if self.cache_key == (start, end_or_count): + asm = self.cache_asm + else: + kwargs = { + 'start_pc': start, + 'count' if relative else 'end_pc': end_or_count + } + asm = gdb.selected_frame().architecture().disassemble(**kwargs) + self.cache_key = (start, end_or_count) + self.cache_asm = asm + # syntax highlight the cached entry + for instr in asm: + instr['asm'] = highlighter.process(instr['asm']) + return asm + +class Variables(Dashboard.Module): + '''Show arguments and locals of the selected frame.''' + + def label(self): + return 'Variables' + + def lines(self, term_width, term_height, style_changed): + return Variables.format_frame( + gdb.selected_frame(), self.show_arguments, self.show_locals, self.compact, self.align, self.sort) + + def attributes(self): + return { + 'arguments': { + 'doc': 'Frame arguments visibility flag.', + 'default': True, + 'name': 'show_arguments', + 'type': bool + }, + 'locals': { + 'doc': 'Frame locals visibility flag.', + 'default': True, + 'name': 'show_locals', + 'type': bool + }, + 'compact': { + 'doc': 'Single-line display flag.', + 'default': True, + 'type': bool + }, + 'align': { + 'doc': 'Align variables in column flag (only if not compact).', + 'default': False, + 'type': bool + }, + 'sort': { + 'doc': 'Sort variables by name.', + 'default': False, + 'type': bool + } + } + + @staticmethod + def format_frame(frame, show_arguments, show_locals, compact, align, sort): + out = [] + # fetch frame arguments and locals + decorator = gdb.FrameDecorator.FrameDecorator(frame) + separator = ansi(', ', R.style_low) + if show_arguments: + def prefix(line): + return Stack.format_line('arg', line) + frame_args = decorator.frame_args() + args_lines = Variables.fetch(frame, frame_args, compact, align, sort) + if args_lines: + if compact: + args_line = separator.join(args_lines) + single_line = prefix(args_line) + out.append(single_line) + else: + out.extend(map(prefix, args_lines)) + if show_locals: + def prefix(line): + return Stack.format_line('loc', line) + frame_locals = decorator.frame_locals() + locals_lines = Variables.fetch(frame, frame_locals, compact, align, sort) + if locals_lines: + if compact: + locals_line = separator.join(locals_lines) + single_line = prefix(locals_line) + out.append(single_line) + else: + out.extend(map(prefix, locals_lines)) + return out + + @staticmethod + def fetch(frame, data, compact, align, sort): + lines = [] + name_width = 0 + if align and not compact: + name_width = max(len(str(elem.sym)) for elem in data) if data else 0 + for elem in data or []: + name = ansi(elem.sym, R.style_high) + ' ' * (name_width - len(str(elem.sym))) + equal = ansi('=', R.style_low) + value = format_value(elem.sym.value(frame), compact) + lines.append('{} {} {}'.format(name, equal, value)) + if sort: + lines.sort() + return lines + +class Stack(Dashboard.Module): + '''Show the current stack trace including the function name and the file location, if available. + +Optionally list the frame arguments and locals too.''' + + def label(self): + return 'Stack' + + def lines(self, term_width, term_height, style_changed): + # skip if the current thread is not stopped + if not gdb.selected_thread().is_stopped(): + return [] + # find the selected frame level (XXX Frame.level() is a recent addition) + start_level = 0 + frame = gdb.newest_frame() + while frame: + if frame == gdb.selected_frame(): + break + frame = frame.older() + start_level += 1 + # gather the frames + more = False + frames = [gdb.selected_frame()] + going_down = True + while True: + # stack frames limit reached + if len(frames) == self.limit: + more = True + break + # zigzag the frames starting from the selected one + if going_down: + frame = frames[-1].older() + if frame: + frames.append(frame) + else: + frame = frames[0].newer() + if frame: + frames.insert(0, frame) + start_level -= 1 + else: + break + else: + frame = frames[0].newer() + if frame: + frames.insert(0, frame) + start_level -= 1 + else: + frame = frames[-1].older() + if frame: + frames.append(frame) + else: + break + # switch direction + going_down = not going_down + # format the output + lines = [] + for number, frame in enumerate(frames, start=start_level): + selected = frame == gdb.selected_frame() + lines.extend(self.get_frame_lines(number, frame, selected)) + # add the placeholder + if more: + lines.append('[{}]'.format(ansi('+', R.style_selected_2))) + return lines + + def attributes(self): + return { + 'limit': { + 'doc': 'Maximum number of displayed frames (0 means no limit).', + 'default': 10, + 'type': int, + 'check': check_ge_zero + }, + 'arguments': { + 'doc': 'Frame arguments visibility flag.', + 'default': False, + 'name': 'show_arguments', + 'type': bool + }, + 'locals': { + 'doc': 'Frame locals visibility flag.', + 'default': False, + 'name': 'show_locals', + 'type': bool + }, + 'compact': { + 'doc': 'Single-line display flag.', + 'default': False, + 'type': bool + }, + 'align': { + 'doc': 'Align variables in column flag (only if not compact).', + 'default': False, + 'type': bool + }, + 'sort': { + 'doc': 'Sort variables by name.', + 'default': False, + 'type': bool + } + } + + def get_frame_lines(self, number, frame, selected=False): + # fetch frame info + style = R.style_selected_1 if selected else R.style_selected_2 + frame_id = ansi(str(number), style) + info = Stack.get_pc_line(frame, style) + frame_lines = [] + frame_lines.append('[{}] {}'.format(frame_id, info)) + # add frame arguments and locals + variables = Variables.format_frame( + frame, self.show_arguments, self.show_locals, self.compact, self.align, self.sort) + frame_lines.extend(variables) + return frame_lines + + @staticmethod + def format_line(prefix, line): + prefix = ansi(prefix, R.style_low) + return '{} {}'.format(prefix, line) + + @staticmethod + def get_pc_line(frame, style): + frame_pc = ansi(format_address(frame.pc()), style) + info = 'from {}'.format(frame_pc) + # if a frame function symbol is available then use it to fetch the + # current function name and address, otherwise fall back relying on the + # frame name + if frame.function(): + name = ansi(frame.function(), style) + func_start = to_unsigned(frame.function().value()) + offset = ansi(str(frame.pc() - func_start), style) + info += ' in {}+{}'.format(name, offset) + elif frame.name(): + name = ansi(frame.name(), style) + info += ' in {}'.format(name) + sal = frame.find_sal() + if sal and sal.symtab: + file_name = ansi(sal.symtab.filename, style) + file_line = ansi(str(sal.line), style) + info += ' at {}:{}'.format(file_name, file_line) + return info + +class History(Dashboard.Module): + '''List the last entries of the value history.''' + + def label(self): + return 'History' + + def lines(self, term_width, term_height, style_changed): + out = [] + # fetch last entries + for i in range(-self.limit + 1, 1): + try: + value = format_value(gdb.history(i)) + value_id = ansi('$${}', R.style_high).format(abs(i)) + equal = ansi('=', R.style_low) + line = '{} {} {}'.format(value_id, equal, value) + out.append(line) + except gdb.error: + continue + return out + + def attributes(self): + return { + 'limit': { + 'doc': 'Maximum number of values to show.', + 'default': 3, + 'type': int, + 'check': check_gt_zero + } + } + +class Memory(Dashboard.Module): + '''Allow to inspect memory regions.''' + + DEFAULT_LENGTH = 16 + + class Region(): + def __init__(self, expression, length, module): + self.expression = expression + self.length = length + self.module = module + self.original = None + self.latest = None + + def reset(self): + self.original = None + self.latest = None + + def format(self, per_line): + # fetch the memory content + try: + address = Memory.parse_as_address(self.expression) + inferior = gdb.selected_inferior() + memory = inferior.read_memory(address, self.length) + # set the original memory snapshot if needed + if not self.original: + self.original = memory + except gdb.error as e: + msg = 'Cannot access {} bytes starting at {}: {}' + msg = msg.format(self.length, self.expression, e) + return [ansi(msg, R.style_error)] + # format the memory content + out = [] + for i in range(0, len(memory), per_line): + region = memory[i:i + per_line] + pad = per_line - len(region) + address_str = format_address(address + i) + # compute changes + hexa = [] + text = [] + for j in range(len(region)): + rel = i + j + byte = memory[rel] + hexa_byte = '{:02x}'.format(ord(byte)) + text_byte = self.module.format_byte(byte) + # differences against the latest have the highest priority + if self.latest and memory[rel] != self.latest[rel]: + hexa_byte = ansi(hexa_byte, R.style_selected_1) + text_byte = ansi(text_byte, R.style_selected_1) + # cumulative changes if enabled + elif self.module.cumulative and memory[rel] != self.original[rel]: + hexa_byte = ansi(hexa_byte, R.style_selected_2) + text_byte = ansi(text_byte, R.style_selected_2) + # format the text differently for clarity + else: + text_byte = ansi(text_byte, R.style_high) + hexa.append(hexa_byte) + text.append(text_byte) + # output the formatted line + hexa_placeholder = ' {}'.format(self.module.placeholder[0] * 2) + text_placeholder = self.module.placeholder[0] + out.append('{} {}{} {}{}'.format( + ansi(address_str, R.style_low), + ' '.join(hexa), ansi(pad * hexa_placeholder, R.style_low), + ''.join(text), ansi(pad * text_placeholder, R.style_low))) + # update the latest memory snapshot + self.latest = memory + return out + + def __init__(self): + self.table = {} + + def label(self): + return 'Memory' + + def lines(self, term_width, term_height, style_changed): + out = [] + for expression, region in self.table.items(): + out.append(divider(term_width, expression)) + out.extend(region.format(self.get_per_line(term_width))) + return out + + def commands(self): + return { + 'watch': { + 'action': self.watch, + 'doc': '''Watch a memory region by expression and length. + +The length defaults to 16 bytes.''', + 'complete': gdb.COMPLETE_EXPRESSION + }, + 'unwatch': { + 'action': self.unwatch, + 'doc': 'Stop watching a memory region by expression.', + 'complete': gdb.COMPLETE_EXPRESSION + }, + 'clear': { + 'action': self.clear, + 'doc': 'Clear all the watched regions.' + } + } + + def attributes(self): + return { + 'cumulative': { + 'doc': 'Highlight changes cumulatively, watch again to reset.', + 'default': False, + 'type': bool + }, + 'full': { + 'doc': 'Take the whole horizontal space.', + 'default': False, + 'type': bool + }, + 'placeholder': { + 'doc': 'Placeholder used for missing items and unprintable characters.', + 'default': '·' + } + } + + def watch(self, arg): + if arg: + expression, _, length_str = arg.partition(' ') + length = Memory.parse_as_address(length_str) if length_str else Memory.DEFAULT_LENGTH + # keep the length when the memory is watched to reset the changes + region = self.table.get(expression) + if region and not length_str: + region.reset() + else: + self.table[expression] = Memory.Region(expression, length, self) + else: + raise Exception('Specify a memory location') + + def unwatch(self, arg): + if arg: + try: + del self.table[arg] + except KeyError: + raise Exception('Memory expression not watched') + else: + raise Exception('Specify a matched memory expression') + + def clear(self, arg): + self.table.clear() + + def format_byte(self, byte): + # `type(byte) is bytes` in Python 3 + if 0x20 < ord(byte) < 0x7f: + return chr(ord(byte)) + else: + return self.placeholder[0] + + def get_per_line(self, term_width): + if self.full: + padding = 3 # two double spaces separator (one is part of below) + elem_size = 4 # HH + 1 space + T + address_length = gdb.parse_and_eval('$pc').type.sizeof * 2 + 2 # 0x + return max(int((term_width - address_length - padding) / elem_size), 1) + else: + return Memory.DEFAULT_LENGTH + + @staticmethod + def parse_as_address(expression): + value = gdb.parse_and_eval(expression) + return to_unsigned(value) + +class Registers(Dashboard.Module): + '''Show the CPU registers and their values.''' + + def __init__(self): + self.table = {} + + def label(self): + return 'Registers' + + def lines(self, term_width, term_height, style_changed): + # skip if the current thread is not stopped + if not gdb.selected_thread().is_stopped(): + return [] + # obtain the registers to display + if style_changed: + self.table = {} + if self.register_list: + register_list = self.register_list.split() + else: + register_list = Registers.fetch_register_list() + # fetch registers status + registers = [] + for name in register_list: + # exclude registers with a dot '.' or parse_and_eval() will fail + if '.' in name: + continue + value = gdb.parse_and_eval('${}'.format(name)) + string_value = Registers.format_value(value) + # exclude unavailable registers (see #255) + if string_value == '': + continue + changed = self.table and (self.table.get(name, '') != string_value) + self.table[name] = string_value + registers.append((name, string_value, changed)) + # handle the empty register list + if not registers: + msg = 'No registers to show (check the "dashboard registers -style list" attribute)' + return [ansi(msg, R.style_error)] + # compute lengths considering an extra space between and around the + # entries (hence the +2 and term_width - 1) + max_name = max(len(name) for name, _, _ in registers) + max_value = max(len(value) for _, value, _ in registers) + max_width = max_name + max_value + 2 + columns = min(int((term_width - 1) / max_width) or 1, len(registers)) + rows = int(math.ceil(float(len(registers)) / columns)) + # build the registers matrix + if self.column_major: + matrix = list(registers[i:i + rows] for i in range(0, len(registers), rows)) + else: + matrix = list(registers[i::columns] for i in range(columns)) + # compute the lengths column wise + max_names_column = list(max(len(name) for name, _, _ in column) for column in matrix) + max_values_column = list(max(len(value) for _, value, _ in column) for column in matrix) + line_length = sum(max_names_column) + columns + sum(max_values_column) + extra = term_width - line_length + # compute padding as if there were one more column + base_padding = int(extra / (columns + 1)) + padding_column = [base_padding] * columns + # distribute the remainder among columns giving the precedence to + # internal padding + rest = extra % (columns + 1) + while rest: + padding_column[rest % columns] += 1 + rest -= 1 + # format the registers + out = [''] * rows + for i, column in enumerate(matrix): + max_name = max_names_column[i] + max_value = max_values_column[i] + for j, (name, value, changed) in enumerate(column): + name = ' ' * (max_name - len(name)) + ansi(name, R.style_low) + style = R.style_selected_1 if changed else '' + value = ansi(value, style) + ' ' * (max_value - len(value)) + padding = ' ' * padding_column[i] + item = '{}{} {}'.format(padding, name, value) + out[j] += item + return out + + def attributes(self): + return { + 'column-major': { + 'doc': 'Show registers in columns instead of rows.', + 'default': False, + 'name': 'column_major', + 'type': bool + }, + 'list': { + 'doc': '''String of space-separated register names to display. + +The empty list (default) causes to show all the available registers. For +architectures different from x86 setting this attribute might be mandatory.''', + 'default': '', + 'name': 'register_list', + } + } + + @staticmethod + def format_value(value): + try: + if value.type.code in [gdb.TYPE_CODE_INT, gdb.TYPE_CODE_PTR]: + int_value = to_unsigned(value, value.type.sizeof) + value_format = '0x{{:0{}x}}'.format(2 * value.type.sizeof) + return value_format.format(int_value) + except (gdb.error, ValueError): + # convert to unsigned but preserve code and flags information + pass + return str(value) + + @staticmethod + def fetch_register_list(*match_groups): + names = [] + for line in run('maintenance print register-groups').split('\n'): + fields = line.split() + if len(fields) != 7: + continue + name, _, _, _, _, _, groups = fields + if not re.match(r'\w', name): + continue + for group in groups.split(','): + if group in (match_groups or ('general',)): + names.append(name) + break + return names + +class Threads(Dashboard.Module): + '''List the currently available threads.''' + + def label(self): + return 'Threads' + + def lines(self, term_width, term_height, style_changed): + out = [] + selected_thread = gdb.selected_thread() + # do not restore the selected frame if the thread is not stopped + restore_frame = gdb.selected_thread().is_stopped() + if restore_frame: + selected_frame = gdb.selected_frame() + # fetch the thread list + threads = [] + for inferior in gdb.inferiors(): + if self.all_inferiors or inferior == gdb.selected_inferior(): + threads += gdb.Inferior.threads(inferior) + for thread in threads: + # skip running threads if requested + if self.skip_running and thread.is_running(): + continue + is_selected = (thread.ptid == selected_thread.ptid) + style = R.style_selected_1 if is_selected else R.style_selected_2 + if self.all_inferiors: + number = '{}.{}'.format(thread.inferior.num, thread.num) + else: + number = str(thread.num) + number = ansi(number, style) + tid = ansi(str(thread.ptid[1] or thread.ptid[2]), style) + info = '[{}] id {}'.format(number, tid) + if thread.name: + info += ' name {}'.format(ansi(thread.name, style)) + # switch thread to fetch info (unless is running in non-stop mode) + try: + thread.switch() + frame = gdb.newest_frame() + info += ' ' + Stack.get_pc_line(frame, style) + except gdb.error: + info += ' (running)' + out.append(info) + # restore thread and frame + selected_thread.switch() + if restore_frame: + selected_frame.select() + return out + + def attributes(self): + return { + 'skip-running': { + 'doc': 'Skip running threads.', + 'default': False, + 'name': 'skip_running', + 'type': bool + }, + 'all-inferiors': { + 'doc': 'Show threads from all inferiors.', + 'default': False, + 'name': 'all_inferiors', + 'type': bool + }, + } + +class Expressions(Dashboard.Module): + '''Watch user expressions.''' + + def __init__(self): + self.table = [] + + def label(self): + return 'Expressions' + + def lines(self, term_width, term_height, style_changed): + out = [] + label_width = 0 + if self.align: + label_width = max(len(expression) for expression in self.table) if self.table else 0 + default_radix = Expressions.get_default_radix() + for number, expression in enumerate(self.table, start=1): + label = expression + match = re.match(r'^/(\d+) +(.+)$', expression) + try: + if match: + radix, expression = match.groups() + run('set output-radix {}'.format(radix)) + value = format_value(gdb.parse_and_eval(expression)) + except gdb.error as e: + value = ansi(e, R.style_error) + finally: + if match: + run('set output-radix {}'.format(default_radix)) + number = ansi(str(number), R.style_selected_2) + label = ansi(expression, R.style_high) + ' ' * (label_width - len(expression)) + equal = ansi('=', R.style_low) + out.append('[{}] {} {} {}'.format(number, label, equal, value)) + return out + + def commands(self): + return { + 'watch': { + 'action': self.watch, + 'doc': 'Watch an expression using the format `[/] `.', + 'complete': gdb.COMPLETE_EXPRESSION + }, + 'unwatch': { + 'action': self.unwatch, + 'doc': 'Stop watching an expression by index.' + }, + 'clear': { + 'action': self.clear, + 'doc': 'Clear all the watched expressions.' + } + } + + def attributes(self): + return { + 'align': { + 'doc': 'Align variables in column flag.', + 'default': False, + 'type': bool + } + } + + def watch(self, arg): + if arg: + if arg not in self.table: + self.table.append(arg) + else: + raise Exception('Expression already watched') + else: + raise Exception('Specify an expression') + + def unwatch(self, arg): + if arg: + try: + number = int(arg) - 1 + except: + number = -1 + if 0 <= number < len(self.table): + self.table.pop(number) + else: + raise Exception('Expression not watched') + else: + raise Exception('Specify an expression') + + def clear(self, arg): + self.table.clear() + + @staticmethod + def get_default_radix(): + try: + return gdb.parameter('output-radix') + except RuntimeError: + # XXX this is a fix for GDB <8.1.x see #161 + message = run('show output-radix') + match = re.match(r'^Default output radix for printing of values is (\d+)\.$', message) + return match.groups()[0] if match else 10 # fallback + +# XXX workaround to support BP_BREAKPOINT in older GDB versions +setattr(gdb, 'BP_CATCHPOINT', getattr(gdb, 'BP_CATCHPOINT', 26)) + +class Breakpoints(Dashboard.Module): + '''Display the breakpoints list.''' + + NAMES = { + gdb.BP_BREAKPOINT: 'break', + gdb.BP_WATCHPOINT: 'watch', + gdb.BP_HARDWARE_WATCHPOINT: 'write watch', + gdb.BP_READ_WATCHPOINT: 'read watch', + gdb.BP_ACCESS_WATCHPOINT: 'access watch', + gdb.BP_CATCHPOINT: 'catch' + } + + def label(self): + return 'Breakpoints' + + def lines(self, term_width, term_height, style_changed): + out = [] + breakpoints = fetch_breakpoints(watchpoints=True, pending=self.show_pending) + for breakpoint in breakpoints: + sub_lines = [] + # format common information + style = R.style_selected_1 if breakpoint['enabled'] else R.style_selected_2 + number = ansi(breakpoint['number'], style) + bp_type = ansi(Breakpoints.NAMES[breakpoint['type']], style) + if breakpoint['temporary']: + bp_type = bp_type + ' {}'.format(ansi('once', style)) + if not R.ansi and breakpoint['enabled']: + bp_type = 'disabled ' + bp_type + line = '[{}] {}'.format(number, bp_type) + if breakpoint['type'] == gdb.BP_BREAKPOINT: + for i, address in enumerate(breakpoint['addresses']): + addr = address['address'] + if i == 0 and addr: + # this is a regular breakpoint + line += ' at {}'.format(ansi(format_address(addr), style)) + # format source information + file_name = address.get('file_name') + file_line = address.get('file_line') + if file_name and file_line: + file_name = ansi(file_name, style) + file_line = ansi(file_line, style) + line += ' in {}:{}'.format(file_name, file_line) + elif i > 0: + # this is a sub breakpoint + sub_style = R.style_selected_1 if address['enabled'] else R.style_selected_2 + sub_number = ansi('{}.{}'.format(breakpoint['number'], i), sub_style) + sub_line = '[{}]'.format(sub_number) + sub_line += ' at {}'.format(ansi(format_address(addr), sub_style)) + # format source information + file_name = address.get('file_name') + file_line = address.get('file_line') + if file_name and file_line: + file_name = ansi(file_name, sub_style) + file_line = ansi(file_line, sub_style) + sub_line += ' in {}:{}'.format(file_name, file_line) + sub_lines += [sub_line] + # format user location + location = breakpoint['location'] + line += ' for {}'.format(ansi(location, style)) + elif breakpoint['type'] == gdb.BP_CATCHPOINT: + what = breakpoint['what'] + line += ' {}'.format(ansi(what, style)) + else: + # format user expression + expression = breakpoint['expression'] + line += ' for {}'.format(ansi(expression, style)) + # format condition + condition = breakpoint['condition'] + if condition: + line += ' if {}'.format(ansi(condition, style)) + # format hit count + hit_count = breakpoint['hit_count'] + if hit_count: + word = 'time{}'.format('s' if hit_count > 1 else '') + line += ' hit {} {}'.format(ansi(breakpoint['hit_count'], style), word) + # append the main line and possibly sub breakpoints + out.append(line) + out.extend(sub_lines) + return out + + def attributes(self): + return { + 'pending': { + 'doc': 'Also show pending breakpoints.', + 'default': True, + 'name': 'show_pending', + 'type': bool + } + } + +# XXX traceback line numbers in this Python block must be increased by 1 +end + +# Better GDB defaults ---------------------------------------------------------- + +set history save +set verbose off +set print pretty on +set print array off +set print array-indexes on +set python print-stack full + +# Start ------------------------------------------------------------------------ + +python Dashboard.start() + +# Fixes ------------------------------------------------------------------------ + +# workaround for the GDB readline issue, see #325 +python import sys; sys.modules['readline'] = None + +# File variables --------------------------------------------------------------- + +# vim: filetype=python +# Local Variables: +# mode: python +# End: diff --git a/.gdbinit b/.gdbinit index 8631f7983..38865486f 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,6 +1,6 @@ file bin/firmware.keepkey.elf set substitute-path /root/keepkey-firmware . set substitute-path /root/libopencm3 ../libopencm3 -target remote localhost:3333 +target extended-remote localhost:3333 monitor reset halt load diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 19ead37bc..9f1f4def1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @markrypt0 @pastaghost \ No newline at end of file +* @markrypt0 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 24e7efbbd..5a9c03ce8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -bin -build +bin/ +build/ .DS_Store .vscode/ +.gdb_history diff --git a/.gitmodules b/.gitmodules index 2d6c4446a..21a2b4b6b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,22 +2,22 @@ path = deps/device-protocol url = https://github.com/keepkey/device-protocol.git branch = master -[submodule "deps/trezor-firmware"] -path = deps/crypto/trezor-firmware -url = https://github.com/keepkey/trezor-firmware.git [submodule "googletest"] path = deps/googletest url = https://github.com/google/googletest.git [submodule "code-signing-keys"] path = code-signing-keys url = https://github.com/keepkey/code-signing-keys.git -[submodule "deps/python-keepkey"] -path = deps/python-keepkey -url = https://github.com/keepkey/python-keepkey.git -branch = master [submodule "deps/qrenc/QR-Code-generator"] path = deps/qrenc/QR-Code-generator url = https://github.com/keepkey/QR-Code-generator.git [submodule "deps/sca-hardening/SecAESSTM32"] path = deps/sca-hardening/SecAESSTM32 -url = https://github.com/keepkey/SecAESSTM32.git +url = https://github.com/markrypt0/SecAESSTM32.git +branch = pi-dev +[submodule "deps/crypto/hw-crypto"] + path = deps/crypto/hw-crypto + url = https://github.com/markrypt0/hw-crypto.git +[submodule "deps/python-keepkey"] + path = deps/python-keepkey + url = https://github.com/keepkey/python-keepkey.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d68c588a..f77485372 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,10 +40,10 @@ if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/googletest/CMakeLists.txt) "googletest missing. Need to 'git submodule update --init --recursive") endif() -if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-firmware/crypto/Makefile) +if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto/crypto/Makefile) message( FATAL_ERROR - " trezor-crypto missing. Need to 'git submodule update --init --recursive" + " hw-crypto missing. Need to 'git submodule update --init --recursive" ) endif() @@ -58,7 +58,7 @@ find_program(NANOPB_GENERATOR nanopb_generator.py) if(${KK_EMULATOR} AND NOT NANOPB_GENERATOR) message( FATAL_ERROR - "Must install nanopb 0.3.9.4, and put nanopb-nanopb-0.3.9.4/generator on your PATH" + "Must install nanopb v1.0.0, and put nanopb-nanopb-v1.0.0/generator on your PATH" ) endif() @@ -69,7 +69,15 @@ else() add_definitions(-DCONFIDENTIAL=__attribute__\(\(section\("confidential"\)\)\)) endif() -add_definitions(-DSTM32F2=1) +if("${DEVDEBUG}" STREQUAL "true") + add_definitions(-DDEV_DEBUG) + add_definitions(-DSTM32F4=1) + if("${TWODISP}" STREQUAL "true") + add_definitions(-DTWO_DISP) + endif() +else() + add_definitions(-DSTM32F2=1) +endif() add_definitions(-DED25519_CUSTOMHASH=1) add_definitions(-DED25519_CUSTOMRANDOM=1) @@ -110,6 +118,10 @@ else() add_definitions(-DDEBUG_LINK=0) endif() +if("${COIN_SUPPORT}" STREQUAL "BTC") + add_definitions(-DBITCOIN_ONLY) +endif() + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") add_definitions(-DDEBUG_ON) add_definitions(-DMEMORY_PROTECT=0) diff --git a/DockerStart.sh b/DockerStart.sh new file mode 100755 index 000000000..f89b10340 --- /dev/null +++ b/DockerStart.sh @@ -0,0 +1 @@ +docker build --build-arg TARGETPLATFORM="amd64/alpine" --build-arg ARCH="amd64" -t kkfirmware:v16 . \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 35d314ace..37ad58133 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,13 @@ -FROM frolvlad/alpine-glibc:glibc-2.27 +ARG TARGETPLATFORM=amd64/alpine +FROM $TARGETPLATFORM -MAINTAINER tech@keepkey.com +ARG ARCH="amd64" + +RUN apk add gcompat -RUN apk add --no-cache python3 py3-pip RUN apk add --update --no-cache \ bzip2-dev \ + xz-dev \ ca-certificates \ git \ openssl \ @@ -12,62 +15,93 @@ RUN apk add --update --no-cache \ tar \ w3m \ unzip \ - py-setuptools \ make \ cmake -RUN pip3 install \ - "MarkupSafe==1.1.1" \ - "ecdsa>=0.9" \ - "protobuf>=3.0.0" \ - "mnemonic>=0.8" \ - requests \ - flask \ - pytest \ - semver - -# Install gcc-arm-none-eabi -WORKDIR /root -RUN wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10-2020q4/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 -RUN tar xvf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 -RUN cp -r gcc-arm-none-eabi-10-2020-q4-major/* /usr/local -RUN rm gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 -RUN rm -rf gcc-arm-none-eabi-10-2020-q4-major +RUN apk add --no-cache py3-setuptools + +# RUN apk add py3-MarkupSafe py3-ecdsa py3-protobuf py3-mnemonic py3-requests py3-flask py3-pytest py3-semver +RUN apk add py3-ecdsa py3-requests py3-flask py3-pytest py3-semver +RUN apk add --update py3-protobuf +RUN apk add --update py3-build + +# Apparently py3-mnemonic is not in the latest version of Alpine packages so get it another way +RUN apk add py3-pip +RUN pip install --break-system-packages --root-user-action ignore mnemonic -# Install protobuf-compiler v3.5.1 WORKDIR /root -RUN mkdir protoc3 -RUN wget https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-x86_64.zip -RUN unzip protoc-3.5.1-linux-x86_64.zip -d protoc3 -RUN mv protoc3/bin/* /usr/local/bin -RUN mv protoc3/include/* /usr/local/include -RUN rm -rf protoc3 + +# FOR ARM BUILD +# the lines similar to "arm-none-eabi-objcopy -w -R .gnu.warning.* libnosys.a" +# are a kludge that patches the system library so that useless system call warnings +# aren't generated, e.g., +# warning: _close is not implemented and will always fail +# during link. The warnings are harmless but there is no way to turn them off with a flag. +# Note that the library is particular to the hardware and no floating point instructions. +# +# see https://stackoverflow.com/questions/73742774/gcc-arm-none-eabi-11-3-is-not-implemented-and-will-always-fail +RUN if [[ "$ARCH" == "arm64v8" ]]; \ + then \ + apk add gcc-arm-none-eabi g++-arm-none-eabi newlib-arm-none-eabi && \ + cd /usr/arm-none-eabi/lib/thumb/v7e-m/nofp && \ + arm-none-eabi-objcopy -w -R .gnu.warning.* libnosys.a && \ + cd /usr/arm-none-eabi/lib/thumb/v7-m/nofp && \ + arm-none-eabi-objcopy -w -R .gnu.warning.* libnosys.a && \ + cd /root && \ + mkdir protoc3 && \ + wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-linux-aarch_64.zip && \ + unzip protoc-3.19.4-linux-aarch_64 -d protoc3 && \ + mv protoc3/bin/* /usr/local/bin && \ + mv protoc3/include/* /usr/local/include && \ + rm -rf protoc3 && \ + rm protoc-3.19.4-linux-aarch_64.zip; \ + fi + +# FOR AMD64 BUILD +# Install gcc-arm-none-eabi and protobuf-compiler +RUN if [[ "$ARCH" == "amd64" ]]; \ + then \ + wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10-2020q4/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 && \ + tar xvf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 && \ + cp -r gcc-arm-none-eabi-10-2020-q4-major/* /usr/local && \ + rm gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 && \ + rm -rf gcc-arm-none-eabi-10-2020-q4-major && \ + mkdir protoc3 && \ + wget https://github.com/google/protobuf/releases/download/v3.19.4/protoc-3.19.4-linux-x86_64.zip && \ + unzip protoc-3.19.4-linux-x86_64.zip -d protoc3 && \ + mv protoc3/bin/* /usr/local/bin && \ + mv protoc3/include/* /usr/local/include && \ + rm -rf protoc3; \ + fi # Install protobuf/python3 support WORKDIR /root -RUN wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-python-3.5.1.zip +RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protobuf-python-3.19.4.zip RUN mkdir protobuf-python -RUN unzip protobuf-python-3.5.1.zip -d protobuf-python +RUN unzip protobuf-python-3.19.4.zip -d protobuf-python -WORKDIR /root/protobuf-python/protobuf-3.5.1/python +WORKDIR /root/protobuf-python/protobuf-3.19.4/python RUN python setup.py install +WORKDIR /root +RUN rm protobuf-python-3.19.4.zip # Install nanopb WORKDIR /root -RUN git clone --branch nanopb-0.3.9.8 https://github.com/nanopb/nanopb/ +RUN git clone --branch v1.0.0 https://github.com/markrypt0/nanopb.git WORKDIR /root/nanopb/generator/proto RUN make RUN rm -rf /root/protobuf-python # Setup environment -ENV PATH /root/nanopb/generator:$PATH +ENV PATH=/root/nanopb/generator:$PATH # Build libopencm3 WORKDIR /root -RUN git clone -b docker-v9 https://github.com/keepkey/libopencm3.git libopencm3 +RUN git clone --branch devdebug-1 https://github.com/markrypt0/libopencm3.git WORKDIR /root/libopencm3 -RUN make +ENV FP_FLAGS="-mfloat-abi=soft" +RUN make TARGETS='stm32/f2 stm32/f4' RUN apk add --update --no-cache \ clang \ diff --git a/README.md b/README.md index 85435a297..26a13d2aa 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,52 @@ -[![CircleCI](https://circleci.com/gh/keepkey/keepkey-firmware.svg?style=svg)](https://circleci.com/gh/keepkey/keepkey-firmware) -## KeepKey Build Procedure - -### Toolchain Installation +## markrypt0-keepkey-firmware development repo -Install Docker Community Edition from: `https://www.docker.com/get-docker` +This is a branch of the keepkey/keepkey-firmware repo that has been refactored to support a simplified crypto library, an ARM build environment, specifically raspberry pi, and also supports new hardware used for a debug environment since all original keepkeys are permanently jtag locked. -``` -$ docker pull kktech/firmware:v5-beta -``` +## KeepKey Build Procedure ### Clone the Source The sources can be obtained from github: ``` -$ git clone git@github.com:keepkey/keepkey-firmware.git +$ git clone git@github.com:markrypt0/markrypt0-keepkey-firmware.git $ git submodule update --init --recursive ``` -### Build +### Toolchain Installation -To build the firmware using the docker container, use the provided script: +The build is done via a docker environment, thus Docker is a reqirement. -``` -$ ./scripts/build/docker/device/release.sh -``` +You must build a local docker image since currently there is no keepkey build image in the docker repo. -## Verifying Published Binaries +Build the image in your dev environment from a shell command line. -Compare the hash of a given tagged build: +For amd64 architecture build environment: ``` -$ git checkout v6.2.0 -$ git submodule update --init --recursive -$ ./scripts/build/docker/device/release.sh -$ tail -c +257 ./bin/firmware.keepkey.bin | shasum -a 256 +$ ./DockerStart.sh ``` -With that of the [signed v6.2.0 binary on github](https://github.com/keepkey/keepkey-firmware/releases/download/v6.2.0/firmware.keepkey.bin), ignoring signatures and firmware metadata: +For arm64v8 architecture build environment: ``` -$ tail -c +257 firmware.keepkey.bin | shasum -a 256 +$ ./armDockerStart.sh ``` -Then inspect the metadata itself by comparing against the structure described [here](https://github.com/keepkey/keepkey-firmware/blob/f20484804285decfacceb71519ae83bc18f2266f/include/keepkey/board/memory.h#L55): +### Build + +To build the firmware using the docker container, use the provided script, for example, to build a debug version of the firmware: ``` -$ head -c +256 signed_firmware.bin | xxd - - +$ ./scripts/build/docker/device/debug.sh ``` -Caveats: +See ./scripts/readme.txt for various build descriptions + +## Verifying Published Binaries -1. v6.2.2 and v6.3.0 had an issue with build reproducibility. See [#212](https://github.com/keepkey/keepkey-firmware/issues/212). -1. As of v6.1.0 and later, we started prepending empty slots for signatures as part of the build, and prior firmwares were emitted without that metadata section. See [87b9ebb84](https://github.com/keepkey/keepkey-firmware/commit/87b9ebb846b241e6357f296e37fd29808ddfa51a) +There are no official keepkey firmware releases build from this repo. ### Docs diff --git a/armDockerStart.sh b/armDockerStart.sh new file mode 100755 index 000000000..8ac2f2f95 --- /dev/null +++ b/armDockerStart.sh @@ -0,0 +1 @@ +docker build --build-arg TARGETPLATFORM="arm64v8/alpine" --build-arg ARCH="arm64v8" -t kkfirmware:v16 . \ No newline at end of file diff --git a/cmake/caches/devdevice.cmake b/cmake/caches/devdevice.cmake new file mode 100644 index 000000000..b51f99612 --- /dev/null +++ b/cmake/caches/devdevice.cmake @@ -0,0 +1,82 @@ +load_cache(docker.cmake) + +# Tell cmake that we're cross compiling for baremetal +set(CMAKE_SYSTEM_NAME Generic CACHE STRING "") +set(CMAKE_CROSSCOMPILING 1) + +include(CMakeForceCompiler) + +# The sysroot. Just assume the one installed in the docker image for now +set(CMAKE_SYSROOT /usr/arm-none-eabi CACHE PATH "") + +set(CMAKE_C_COMPILER arm-none-eabi-gcc CACHE FILEPATH "") +set(CMAKE_CXX_COMPILER arm-none-eabi-g++ CACHE FILEPATH "") + +# FIXME: teach cmake how to do compiler tests on baremetal +set(CMAKE_C_COMPILER_WORKS True CACHE BOOL "") +set(CMAKE_CXX_COMPILER_WORKS True CACHE BOOL "") + +set(floatarch -mfloat-abi=soft) +set(fpu -mfpu=fpv4-sp-d16) +set(cpu -mcpu=cortex-m4) + + +set(ARCH_FLAGS + "-mthumb \ + ${cpu} \ + ${fpu} \ + ${floatarch} \ + -ffunction-sections \ + -fdata-sections \ + -fno-common \ + -fstack-protector-all" CACHE STRING "") + +set(WARN_FLAGS + "-Wall \ + -Wextra \ + -Wformat \ + -Wformat-nonliteral \ + -Wformat-security \ + -Wimplicit-function-declaration \ + -Winit-self \ + -Wmultichar \ + -Wpointer-arith \ + -Wredundant-decls \ + -Wreturn-type \ + -Wshadow \ + -Wsign-compare \ + -Wstrict-prototypes \ + -Wundef \ + -Wuninitialized \ + -Werror") + + +set(KK_C_FLAGS "${ARCH_FLAGS} -std=gnu99 ${WARN_FLAGS}" CACHE STRING "") +set(KK_CXX_FLAGS "${ARCH_FLAGS} -std=gnu++11 ${WARN_FLAGS} \ + -fno-exceptions \ + -fno-rtti \ + -fno-threadsafe-statics \ + -fuse-cxa-atexit \ + -Woverloaded-virtual \ + -Weffc++" CACHE STRING "") + +set(CMAKE_C_FLAGS_DEBUG "${KK_C_FLAGS} -Os -g" CACHE STRING "") +set(CMAKE_C_FLAGS_MINSIZEREL "${KK_C_FLAGS} -Os" CACHE STRING "") +set(CMAKE_C_FLAGS_RELEASE "${KK_C_FLAGS} -Os" CACHE STRING "") +set(CMAKE_CXX_FLAGS_DEBUG "${KK_CXX_FLAGS} -Os -g" CACHE STRING "") +set(CMAKE_CXX_FLAGS_MINSIZEREL "${KK_CXX_FLAGS} -Os" CACHE STRING "") +set(CMAKE_CXX_FLAGS_RELEASE "${KK_CXX_FLAGS} -Os" CACHE STRING "") + +set(CMAKE_ASM_FLAGS "${cpu} \ + -mthumb \ + -x assembler-with-cpp \ + -gdwarf-2" CACHE STRING "") + +set(CMAKE_EXE_LINKER_FLAGS + "-mthumb \ + ${cpu} \ + ${fpu} \ + -nostartfiles \ + ${floatarch} \ + -specs=nosys.specs \ + -Wl,--gc-sections" CACHE STRING "") diff --git a/cmake/caches/device.cmake b/cmake/caches/device.cmake index 436a23bce..9d76fdb2e 100644 --- a/cmake/caches/device.cmake +++ b/cmake/caches/device.cmake @@ -16,10 +16,14 @@ set(CMAKE_CXX_COMPILER arm-none-eabi-g++ CACHE FILEPATH "") set(CMAKE_C_COMPILER_WORKS True CACHE BOOL "") set(CMAKE_CXX_COMPILER_WORKS True CACHE BOOL "") + +set(floatarch -msoft-float) +set(cpu -mcpu=cortex-m3) + set(ARCH_FLAGS "-mthumb \ - -mcpu=cortex-m3 \ - -msoft-float \ + ${cpu} \ + ${floatarch} \ -ffunction-sections \ -fdata-sections \ -fno-common \ @@ -61,15 +65,16 @@ set(CMAKE_CXX_FLAGS_DEBUG "${KK_CXX_FLAGS} -Os -g" CACHE STRING "") set(CMAKE_CXX_FLAGS_MINSIZEREL "${KK_CXX_FLAGS} -Os" CACHE STRING "") set(CMAKE_CXX_FLAGS_RELEASE "${KK_CXX_FLAGS} -Os" CACHE STRING "") -set(CMAKE_ASM_FLAGS "-mcpu=cortex-m3 \ +set(CMAKE_ASM_FLAGS "${cpu} \ -mthumb \ -x assembler-with-cpp \ -gdwarf-2" CACHE STRING "") set(CMAKE_EXE_LINKER_FLAGS "-mthumb \ - -mcpu=cortex-m3 \ + ${cpu} \ + ${fpu} \ -nostartfiles \ - -msoft-float \ + ${floatarch} \ -specs=nosys.specs \ -Wl,--gc-sections" CACHE STRING "") diff --git a/deps/crypto/CMakeLists.txt b/deps/crypto/CMakeLists.txt index cd735668c..87104713b 100644 --- a/deps/crypto/CMakeLists.txt +++ b/deps/crypto/CMakeLists.txt @@ -1,76 +1,76 @@ set(sources - trezor-firmware/crypto/bip39.c - trezor-firmware/crypto/bip39_english.c - trezor-firmware/crypto/hmac.c - trezor-firmware/crypto/hmac_drbg.c - trezor-firmware/crypto/sha2.c - trezor-firmware/crypto/base32.c - trezor-firmware/crypto/hasher.c - #trezor-firmware/crypto/tools/bip39bruteforce.c - #trezor-firmware/crypto/tools/mktable.c - #trezor-firmware/crypto/tools/xpubaddrgen.c - trezor-firmware/crypto/rand.c - trezor-firmware/crypto/rc4.c - trezor-firmware/crypto/blake256.c - trezor-firmware/crypto/cash_addr.c - trezor-firmware/crypto/curves.c - trezor-firmware/crypto/rfc6979.c - trezor-firmware/crypto/ed25519-donna/curve25519-donna-scalarmult-base.c - trezor-firmware/crypto/ed25519-donna/ed25519-donna-32bit-tables.c - trezor-firmware/crypto/ed25519-donna/ed25519.c - trezor-firmware/crypto/ed25519-donna/curve25519-donna-helpers.c - trezor-firmware/crypto/ed25519-donna/ed25519-blake2b.c - trezor-firmware/crypto/ed25519-donna/ed25519-keccak.c - trezor-firmware/crypto/ed25519-donna/ed25519-sha3.c - trezor-firmware/crypto/ed25519-donna/ed25519-donna-basepoint-table.c - trezor-firmware/crypto/ed25519-donna/modm-donna-32bit.c - trezor-firmware/crypto/ed25519-donna/curve25519-donna-32bit.c - trezor-firmware/crypto/ed25519-donna/ed25519-donna-impl-base.c - trezor-firmware/crypto/nem.c - trezor-firmware/crypto/nano.c - #trezor-firmware/crypto/tests/test_check.c - #trezor-firmware/crypto/tests/test_openssl.c - #trezor-firmware/crypto/tests/test_speed.c - trezor-firmware/crypto/secp256k1.c - trezor-firmware/crypto/bignum.c - trezor-firmware/crypto/segwit_addr.c - trezor-firmware/crypto/ripemd160.c - trezor-firmware/crypto/groestl.c - trezor-firmware/crypto/nist256p1.c - #trezor-firmware/crypto/monero/serialize.c - #trezor-firmware/crypto/monero/range_proof.c - #trezor-firmware/crypto/monero/xmr.c - #trezor-firmware/crypto/monero/base58.c - trezor-firmware/crypto/memzero.c - trezor-firmware/crypto/blake2b.c - trezor-firmware/crypto/script.c - trezor-firmware/crypto/bip32.c - trezor-firmware/crypto/address.c - trezor-firmware/crypto/blake2s.c - trezor-firmware/crypto/sha3.c - trezor-firmware/crypto/base58.c - trezor-firmware/crypto/ecdsa.c - trezor-firmware/crypto/pbkdf2.c - trezor-firmware/crypto/aes/aeskey.c - trezor-firmware/crypto/aes/aescrypt.c - trezor-firmware/crypto/aes/aes_modes.c - #trezor-firmware/crypto/aes/aestst.c - trezor-firmware/crypto/aes/aestab.c) + hw-crypto/crypto/bip39.c + hw-crypto/crypto/bip39_english.c + hw-crypto/crypto/hmac.c + hw-crypto/crypto/hmac_drbg.c + hw-crypto/crypto/sha2.c + hw-crypto/crypto/base32.c + hw-crypto/crypto/hasher.c + #hw-crypto/crypto/tools/bip39bruteforce.c + #hw-crypto/crypto/tools/mktable.c + #hw-crypto/crypto/tools/xpubaddrgen.c + hw-crypto/crypto/rand.c + hw-crypto/crypto/rc4.c + hw-crypto/crypto/blake256.c + hw-crypto/crypto/cash_addr.c + hw-crypto/crypto/curves.c + hw-crypto/crypto/rfc6979.c + hw-crypto/crypto/ed25519-donna/curve25519-donna-scalarmult-base.c + hw-crypto/crypto/ed25519-donna/ed25519-donna-32bit-tables.c + hw-crypto/crypto/ed25519-donna/ed25519.c + hw-crypto/crypto/ed25519-donna/curve25519-donna-helpers.c + hw-crypto/crypto/ed25519-donna/ed25519-blake2b.c + hw-crypto/crypto/ed25519-donna/ed25519-keccak.c + hw-crypto/crypto/ed25519-donna/ed25519-sha3.c + hw-crypto/crypto/ed25519-donna/ed25519-donna-basepoint-table.c + hw-crypto/crypto/ed25519-donna/modm-donna-32bit.c + hw-crypto/crypto/ed25519-donna/curve25519-donna-32bit.c + hw-crypto/crypto/ed25519-donna/ed25519-donna-impl-base.c + hw-crypto/crypto/nem.c + hw-crypto/crypto/nano.c + #hw-crypto/crypto/tests/test_check.c + #hw-crypto/crypto/tests/test_openssl.c + #hw-crypto/crypto/tests/test_speed.c + hw-crypto/crypto/secp256k1.c + hw-crypto/crypto/bignum.c + hw-crypto/crypto/segwit_addr.c + hw-crypto/crypto/ripemd160.c + hw-crypto/crypto/groestl.c + hw-crypto/crypto/nist256p1.c + #hw-crypto/crypto/monero/serialize.c + #hw-crypto/crypto/monero/range_proof.c + #hw-crypto/crypto/monero/xmr.c + #hw-crypto/crypto/monero/base58.c + hw-crypto/crypto/memzero.c + hw-crypto/crypto/blake2b.c + hw-crypto/crypto/script.c + hw-crypto/crypto/bip32.c + hw-crypto/crypto/address.c + hw-crypto/crypto/blake2s.c + hw-crypto/crypto/sha3.c + hw-crypto/crypto/base58.c + hw-crypto/crypto/ecdsa.c + hw-crypto/crypto/pbkdf2.c + hw-crypto/crypto/aes/aeskey.c + hw-crypto/crypto/aes/aescrypt.c + hw-crypto/crypto/aes/aes_modes.c + #hw-crypto/crypto/aes/aestst.c + hw-crypto/crypto/aes/aestab.c) # Clang 5.0 in the docker image (kktech/firmware:v7) is missing # , which breaks these. Until they're needed, we'll just elide # them. - #trezor-firmware/crypto/chacha20poly1305/chacha_merged.c - #trezor-firmware/crypto/chacha20poly1305/rfc7539.c - #trezor-firmware/crypto/chacha20poly1305/poly1305-donna.c - #trezor-firmware/crypto/chacha20poly1305/chacha20poly1305.c + #hw-crypto/crypto/chacha20poly1305/chacha_merged.c + #hw-crypto/crypto/chacha20poly1305/rfc7539.c + #hw-crypto/crypto/chacha20poly1305/poly1305-donna.c + #hw-crypto/crypto/chacha20poly1305/chacha20poly1305.c include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/trezor-firmware/crypto - ${CMAKE_CURRENT_SOURCE_DIR}/trezor-firmware/ - ${CMAKE_CURRENT_SOURCE_DIR}/trezor-firmware/crypto/ed25519-donna + ${CMAKE_CURRENT_SOURCE_DIR}/hw-crypto/crypto + ${CMAKE_CURRENT_SOURCE_DIR}/hw-crypto/ + ${CMAKE_CURRENT_SOURCE_DIR}/hw-crypto/crypto/ed25519-donna ${OPENSSL_INCLUDE_DIR}) -add_library(trezorcrypto ${sources}) +add_library(hwcrypto ${sources}) diff --git a/deps/crypto/hw-crypto b/deps/crypto/hw-crypto new file mode 160000 index 000000000..34dbef959 --- /dev/null +++ b/deps/crypto/hw-crypto @@ -0,0 +1 @@ +Subproject commit 34dbef959435dcab8b13e3d4776294cc762e12e5 diff --git a/deps/crypto/trezor-firmware b/deps/crypto/trezor-firmware deleted file mode 160000 index 03d8a55a8..000000000 --- a/deps/crypto/trezor-firmware +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 03d8a55a832fb61bb89477ef7239a80ecb367080 diff --git a/deps/device-protocol b/deps/device-protocol index 323802f17..0738386f2 160000 --- a/deps/device-protocol +++ b/deps/device-protocol @@ -1 +1 @@ -Subproject commit 323802f17dd44165a5100357df771348c8b49672 +Subproject commit 0738386f2c8a40ac1ef1b323484cffda371fef62 diff --git a/deps/googletest b/deps/googletest index 7888184f2..4902ea2d7 160000 --- a/deps/googletest +++ b/deps/googletest @@ -1 +1 @@ -Subproject commit 7888184f28509dba839e3683409443e0b5bb8948 +Subproject commit 4902ea2d7c6faed89b6facee00baa34bb108fc0d diff --git a/deps/python-keepkey b/deps/python-keepkey index 9e4b4f0b1..bc603d2bf 160000 --- a/deps/python-keepkey +++ b/deps/python-keepkey @@ -1 +1 @@ -Subproject commit 9e4b4f0b14a6ea966fecfe31d8e2706abe7b405c +Subproject commit bc603d2bf309666173dd2fe36d24d9da30e30f02 diff --git a/deps/sca-hardening/SecAESSTM32 b/deps/sca-hardening/SecAESSTM32 index 71d356a11..9fb262cd4 160000 --- a/deps/sca-hardening/SecAESSTM32 +++ b/deps/sca-hardening/SecAESSTM32 @@ -1 +1 @@ -Subproject commit 71d356a1141624994cf613bd2d2583892e8e6d5a +Subproject commit 9fb262cd41467f7329e1abce3a6d7e057d0d9917 diff --git a/deps/sca-hardening/aes128_cbc.c b/deps/sca-hardening/aes128_cbc.c index 31d9bfce2..a85203c6e 100644 --- a/deps/sca-hardening/aes128_cbc.c +++ b/deps/sca-hardening/aes128_cbc.c @@ -1,6 +1,6 @@ #include "aes_sca/aes.h" -#include "trezor/crypto/aes/aes.h" -#include "trezor/crypto/memzero.h" +#include "hwcrypto/crypto/aes/aes.h" +#include "hwcrypto/crypto/memzero.h" #include diff --git a/deps/sca-hardening/include/aes_sca/aes128_cbc.h b/deps/sca-hardening/include/aes_sca/aes128_cbc.h index 97a3d1d99..dc914cad4 100644 --- a/deps/sca-hardening/include/aes_sca/aes128_cbc.h +++ b/deps/sca-hardening/include/aes_sca/aes128_cbc.h @@ -1,7 +1,7 @@ #ifndef AES128_CBC_H #define AES128_CBC_H -#include "trezor/crypto/aes/aes.h" +#include "hwcrypto/crypto/aes/aes.h" AES_RETURN aes128_cbc_sca_encrypt( const unsigned char *key, diff --git a/docs/ArmBuild.md b/docs/ArmBuild.md new file mode 100755 index 000000000..2672196ca --- /dev/null +++ b/docs/ArmBuild.md @@ -0,0 +1,51 @@ + + +Overview +-------- + +It is possible to build the firmware on an ARM-based system with the pi-dev branch. It was developed specifically for a raspbery pi 5 running Bookworm but in general should work for any ARM-based build system. +The standard build uses a docker container to isolate the raspi os, but theoretically this is not required. The dockerized environment has some patches to surpress moot warnings from the linker and does some other tricks to make the build clean. +The pi-dev branch has been refactored to simplify the build, namely the crypto library. No longer is the entire trezor monorepo pulled into the firmware build just to use some of the trezor crypto library. The crypto library submodule is now called hw-crypto, which was copied directly from the keepkey branch of the trezor crypto directory. + +Requirements for pi environment +------------------------------- +docker +docker-compose (if you want to run the emulator for testing) + +Install +------- +```sh +$ git clone https://github.com/markrypt0/keepkey-firmware.git +$ git checkout pi-dev +$ git submodule update --init --recursive +``` + +Build the docker container from keepkey-firmware root directory +```sh +$ ./armDockerStart +``` + +Build firmware +```sh +$ ./scripts/build/docker/device/debug.sh +``` + +## Using Keepkey on Raspberry Pi + +You'll need to install UDEV rules in order to allow non-root users to communicate with the device. The rules for a Raspberry Pi are slightly different than what is described for linux in Host.md + +Add the following udev rules to `/etc/udev/rules.d/51-usb-keepkey.rules`: + +``` +# KeepKey: Your Private Bitcoin Vault +# http://www.keepkey.com/ +# Put this file into /etc/udev/rules.d/ on a Raspberry Pi + +# KeepKey HID Firmware/Bootloader +SUBSYSTEM=="usb", ATTR{idVendor}=="2b24", ATTR{idProduct}=="0001", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="keepkey%n" +KERNEL=="hiddev*", ATTRS{idVendor}=="2b24", ATTRS{idProduct}=="0001", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl" + +# KeepKey WebUSB Firmware/Bootloader +SUBSYSTEM=="usb", ATTR{idVendor}=="2b24", ATTR{idProduct}=="0002", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="keepkey%n" +KERNEL=="hiddev*", ATTRS{idVendor}=="2b24", ATTRS{idProduct}=="0002", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl" +``` \ No newline at end of file diff --git a/fuzzer/firmware/ripple_decode.cpp b/fuzzer/firmware/ripple_decode.cpp index 93664a2fa..b4203c380 100644 --- a/fuzzer/firmware/ripple_decode.cpp +++ b/fuzzer/firmware/ripple_decode.cpp @@ -2,7 +2,7 @@ extern "C" { #include "keepkey/firmware/coins.h" #include "keepkey/firmware/ripple.h" #include "keepkey/firmware/ripple_base58.h" -#include "trezor/crypto/hasher.h" +#include "hwcrypto/crypto/hasher.h" } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { diff --git a/include/hwcrypto/crypto b/include/hwcrypto/crypto new file mode 120000 index 000000000..1d5a23bff --- /dev/null +++ b/include/hwcrypto/crypto @@ -0,0 +1 @@ +../../deps/crypto/hw-crypto/crypto/ \ No newline at end of file diff --git a/include/keepkey/board/keepkey_board.h b/include/keepkey/board/keepkey_board.h index dd4fe5248..a3c18b9c9 100644 --- a/include/keepkey/board/keepkey_board.h +++ b/include/keepkey/board/keepkey_board.h @@ -27,8 +27,8 @@ #include "keepkey/board/timer.h" #include "keepkey/board/usb.h" #include "keepkey/crypto/curves.h" -#include "trezor/crypto/bip32.h" -#include "trezor/crypto/curves.h" +#include "hwcrypto/crypto/bip32.h" +#include "hwcrypto/crypto/curves.h" /* storage layout: diff --git a/include/keepkey/board/layout.h b/include/keepkey/board/layout.h index b3be90b96..cb646dcb6 100644 --- a/include/keepkey/board/layout.h +++ b/include/keepkey/board/layout.h @@ -29,6 +29,7 @@ #define MAX_ANIMATIONS 5 #define ANIMATION_PERIOD 20 + /* Vertical Alignment */ #define ONE_LINE 1 #define TWO_LINES 2 diff --git a/include/keepkey/board/memory.h b/include/keepkey/board/memory.h index 6e47126a1..f443a2bf9 100644 --- a/include/keepkey/board/memory.h +++ b/include/keepkey/board/memory.h @@ -21,7 +21,7 @@ #define MEMORY_H //#include -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/sha2.h" #include #include diff --git a/include/keepkey/board/messages.h b/include/keepkey/board/messages.h index 7fca69570..a58ba5cd3 100644 --- a/include/keepkey/board/messages.h +++ b/include/keepkey/board/messages.h @@ -77,7 +77,7 @@ typedef enum { IN_MSG, OUT_MSG } MessageMapDirection; typedef enum { PARSABLE, RAW } MessageMapDispatch; typedef struct { - const pb_field_t *fields; + const pb_msgdesc_t *fields; msg_handler_t process_func; MessageMapDispatch dispatch; MessageMapType type; @@ -99,8 +99,8 @@ typedef enum { typedef void (*raw_msg_handler_t)(RawMessage *msg, uint32_t frame_length); -const pb_field_t *message_fields(MessageMapType type, MessageType msg_id, - MessageMapDirection dir); +const pb_msgdesc_t *message_fields(MessageMapType type, MessageType msg_id, + MessageMapDirection dir); bool msg_write(MessageType msg_id, const void *msg); @@ -129,6 +129,7 @@ MessageType wait_for_tiny_msg(uint8_t *buf); MessageType check_for_tiny_msg(uint8_t *buf); uint32_t parse_pb_varint(RawMessage *msg, uint8_t varint_count); -int encode_pb(const void *source_ptr, const pb_field_t *fields, uint8_t *buffer, + +int encode_pb(const void *source_ptr, const pb_msgdesc_t *fields, uint8_t *buffer, uint32_t len); #endif diff --git a/include/keepkey/board/pin.h b/include/keepkey/board/pin.h index 8a07a32cb..54ce9f168 100644 --- a/include/keepkey/board/pin.h +++ b/include/keepkey/board/pin.h @@ -51,6 +51,26 @@ typedef struct { uint16_t pin; } Pin; +#ifndef EMULATOR + +static const Pin nDC_PIN = {GPIOC, GPIO2}; +static const Pin nSEL_PIN = {GPIOC, GPIO5}; +static const Pin nRESET_PIN = {GPIOC, GPIO3}; + +#ifndef DEV_DEBUG +// display signals for standard keepkey +static const Pin nOE_PIN = {GPIOA, GPIO8}; +static const Pin nWE_PIN = {GPIOA, GPIO9}; +static const Pin BACKLIGHT_PWR_PIN = {GPIOB, GPIO0}; +#endif +#endif // EMULATOR + + +#ifdef DEV_DEBUG +static const Pin SCOPE_PIN = {GPIOC, GPIO7}; +#endif + + void pin_init_output(const Pin *pin, OutputMode output_mode, PullMode pull_mode); diff --git a/include/keepkey/board/ssd1351/fonts.h b/include/keepkey/board/ssd1351/fonts.h new file mode 100644 index 000000000..813291ff2 --- /dev/null +++ b/include/keepkey/board/ssd1351/fonts.h @@ -0,0 +1,18 @@ +/* vim: set ai et ts=4 sw=4: */ +#ifndef __FONTS_H__ +#define __FONTS_H__ + +#include + +typedef struct { + const uint8_t width; + uint8_t height; + const uint16_t *data; +} FontDef; + + +extern FontDef Font_7x10; +extern FontDef Font_11x18; +extern FontDef Font_16x26; + +#endif // __FONTS_H__ diff --git a/include/keepkey/board/ssd1351/ssd1351.h b/include/keepkey/board/ssd1351/ssd1351.h new file mode 100644 index 000000000..374bca039 --- /dev/null +++ b/include/keepkey/board/ssd1351/ssd1351.h @@ -0,0 +1,45 @@ +#ifndef __SSD1351_H__ +#define __SSD1351_H__ + +#include "keepkey/board/ssd1351/fonts.h" +#include "keepkey/board/pin.h" +#include + + +#define SSD1351_SPI_PORT SPI1 + +#define SSD1351_RES_Pin nRESET_PIN +#define SSD1351_DC_Pin nDC_PIN +static const Pin SSD1351_CS_Pin = {GPIOC, GPIO4}; + +// default orientation +#define SSD1351_WIDTH 128 +#define SSD1351_HEIGHT 96 + +/****************************/ + +// Color definitions +#define SSD1351_BLACK 0x0000 +#define SSD1351_BLUE 0x001F +#define SSD1351_RED 0xF800 +#define SSD1351_GREEN 0x07E0 +#define SSD1351_CYAN 0x07FF +#define SSD1351_MAGENTA 0xF81F +#define SSD1351_YELLOW 0xFFE0 +#define SSD1351_WHITE 0xFFFF +#define SSD1351_COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)) + +// call before initializing any SPI devices +void SSD1351_Unselect(void); + +void SSD1351_CSInit(void); +void SSD1351_Init(void); +void SSD1351_DrawPixel(uint16_t x, uint16_t y, uint16_t color); +void SSD1351_WriteString(uint16_t x, uint16_t y, const char* str, FontDef font, uint16_t color, uint16_t bgcolor); +void SSD1351_FillRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color); +void SSD1351_FillScreen(uint16_t color); +void SSD1351_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data); +void SSD1351_InvertColors(bool invert); +void SSD1351_SetRotation(uint8_t r); + +#endif // __SSD1351_H__ diff --git a/include/keepkey/board/ssd1351/testimg.h b/include/keepkey/board/ssd1351/testimg.h new file mode 100644 index 000000000..3b73704a4 --- /dev/null +++ b/include/keepkey/board/ssd1351/testimg.h @@ -0,0 +1,130 @@ +const uint16_t test_img_128x128[][128] = { +{0x0B63,0x0B63,0x0B6B,0x2B6B,0x2B6B,0x2C6B,0x2B6B,0x2C6B,0x2C6B,0x4C6B,0xCC73,0xCB8C,0xCBA5,0xECB6,0xCCCF,0xCDDF,0xCCEF,0xCBF7,0xCAF7,0xCBF7,0xCCFF,0xCBFF,0xCCF7,0xCDEF,0xCCDF,0xACDF,0x6DD7,0xDBB6,0xBFB6,0xDFBE,0x5FA6,0x7D85,0x7C64,0xDC53,0x3D74,0x1F8D,0x9F9D,0xDFA5,0xBFA5,0x9E74,0xB95B,0x3653,0xD552,0xF452,0x1453,0xB14A,0x734A,0x1A5B,0xBC6B,0xFD94,0xDD7C,0xDEAD,0x1FD7,0xBFBE,0x7B95,0x5A74,0x7A94,0xDD94,0xDB8C,0xD894,0xF794,0x5484,0x147C,0x347C,0x3484,0xB47B,0x7A73,0xDFAD,0x7FB6,0xFFA5,0x5D8D,0xF14A,0xD231,0xD539,0x5D7C,0x7FA5,0xFFC6,0x3E9E,0x7C6C,0x7E7C,0x1E6C,0x7C4B,0xDD63,0x3B4B,0x7D53,0xFC42,0x9E84,0x3FB6,0xFFA5,0xDF84,0x9F7C,0x9E74,0x9E7C,0x3F95,0x9EAE,0xB97C,0x7B63,0x3E6C,0xDE8C,0x7FC6,0x9FBE,0x7FAE,0x5E9D,0x1FD7,0x9CAE,0xAD5B,0xCA62,0xAA5A,0x8A5A,0x895A,0x695A,0x6952,0x4952,0x4852,0x484A,0x284A,0x084A,0x084A,0x0742,0xE741,0xC741,0xC639,0xC639,0xA639,0x8631,0x8531,0x6531,0x6531,}, +{0x0B63,0x2B6B,0x0B6B,0x0B6B,0x2B6B,0x2C6B,0x2B6B,0x2C6B,0x4B6B,0xEC73,0x4B8D,0xAAAE,0x6AC7,0xCBDF,0xECE7,0xCCEF,0xCBF7,0xCAFF,0xCAFF,0xEBFF,0xECFF,0xCCFF,0xCEF7,0xD0E7,0xAEC7,0x8D96,0xAD7D,0x5475,0x7E85,0x1FAE,0x7FB6,0x1FA6,0xBE7C,0x1C6C,0xFC73,0x1D6C,0x1D74,0x9E7C,0xFF8C,0x5B74,0x1864,0x9663,0xD04A,0x6E4A,0x704A,0xF35A,0xF152,0x8F42,0xAF39,0xD64A,0xFD94,0x1EA6,0x1D8D,0x3D9D,0x3FB6,0x3FAE,0x7C95,0x5874,0x5A84,0xBB8C,0x9B8C,0xDC94,0x5684,0x347C,0xD17B,0xB17B,0x306B,0x586B,0x3E9D,0xDF9D,0x1F85,0x1D74,0x154B,0xB442,0xD431,0x9D63,0x9F84,0x1FAE,0xBFB6,0x9F8D,0xFD5B,0xFA4A,0xDA3A,0xFB42,0x7D53,0xFB42,0x1C43,0x1C7C,0xFFC6,0x1F9E,0xFE63,0x5E74,0x7E7C,0x7E84,0x1FB6,0xFFB6,0xDC74,0x3C5B,0xDD63,0x7F9D,0x9FBE,0x7FBE,0x7FAE,0x1FBE,0x3FCF,0x9BAE,0x8E6B,0xD68C,0xCA5A,0xAA5A,0x895A,0x8952,0x6952,0x6952,0x484A,0x484A,0x284A,0x284A,0x2742,0x0742,0xE741,0xC641,0xC639,0xA639,0xA639,0x8531,0x8531,0x6531,}, +{0x0B6B,0x0B6B,0x0B6B,0x0B6B,0x2B6B,0x2B6B,0x2C6B,0x2C6B,0xCC73,0xEEA5,0x8FD7,0xCED7,0xCDDF,0xCDE7,0xCDEF,0xCCF7,0xCBFF,0xCBFF,0xEBFF,0xCCFF,0xCCFF,0xCDF7,0xCEF7,0xD1DF,0xB1CF,0x3096,0x317D,0xD37C,0x3C85,0x5F8D,0xBF9D,0xDFAD,0x1F8D,0x7C74,0x1C85,0xFC7C,0xDC74,0xFA5B,0xDB6B,0xFC9C,0x7B7C,0x5874,0xF873,0x9563,0x8C4A,0x6E4A,0x704A,0x2F42,0x2F3A,0x9452,0xBC8C,0xBEA5,0xFFAD,0x3FAE,0x7FB6,0xBFBE,0xBFB6,0x1FA6,0x5D8D,0x7B84,0x3B84,0x7B7C,0xFD94,0xB984,0xD47B,0x306B,0x3163,0x4B52,0x556B,0x1E8D,0x7D74,0xFE8C,0xBB7C,0x9442,0x3853,0xB73A,0x7842,0xFE73,0x3F95,0x5FAE,0xFF95,0x5D64,0xFA4A,0x1953,0xB83A,0x9A42,0xBA42,0x3B4B,0xFD9C,0x5FCF,0x3F9E,0x1C64,0x5C74,0xFB6B,0xFE8C,0x7FC6,0x1FBF,0x3E7D,0xBB63,0x1E6C,0x9FA5,0x1FB6,0x3FB6,0xFFA5,0xDFCE,0x3FC7,0x7785,0xBDAD,0xD895,0xCA5A,0xAA5A,0x895A,0x695A,0x6952,0x6952,0x6852,0x4852,0x484A,0x284A,0x0842,0x0742,0xE741,0xE741,0xC639,0xA639,0xA631,0x8631,0x8531,}, +{0x0B6B,0x0B6B,0x2B6B,0x2B6B,0x2B6B,0x0B73,0x2B6B,0x4C6B,0xCC73,0x4D9D,0x10CF,0xD0E7,0xCEEF,0xEDF7,0xCDF7,0xCBFF,0xCBFF,0xCBFF,0xECFF,0xEBFF,0xCCFF,0xCDFF,0xD1EF,0xD2DF,0x92BF,0x519E,0x7595,0xD784,0xBFA5,0x9FBE,0x5FAE,0xFF9D,0x5F95,0xFF84,0x9FA5,0x5FBE,0xDFC6,0xFFBE,0x5EA6,0x7B8D,0xF85B,0x1A7C,0x7B84,0xD873,0x1984,0x315B,0x9252,0xF352,0x1153,0x525B,0x5353,0xD74A,0xBD94,0x9FC6,0xBFBE,0x3FA6,0x1FB6,0x1FCF,0xDFBE,0xFFA5,0x1B5C,0x1B7C,0x5C7C,0x9D8C,0x1A7C,0xB56B,0x335B,0x9052,0xB652,0x5F9D,0x9FB6,0x7E8D,0x3D74,0xFB63,0xD853,0xD94A,0xB942,0x1B4B,0xDC4A,0xBF84,0xFFA5,0xDF95,0x1C5C,0xF942,0x9942,0xD942,0x983A,0xFB4A,0x1C43,0x7D8C,0xFFC6,0xDE95,0xBC84,0x7D95,0xDE9D,0x1F95,0x5FBE,0xFFB6,0x7E8D,0x7E95,0x7D74,0x7F9D,0xDFA5,0xFFA5,0x3FB6,0x1FCF,0xDFBE,0x9FB6,0xFFCE,0x788D,0xCB5A,0xAA5A,0xAA5A,0x6952,0x6952,0x6952,0x484A,0x4852,0x284A,0x284A,0x074A,0x0742,0xE741,0xE739,0xC639,0xC639,0xA639,0xA531,}, +{0x0B6B,0x0B6B,0x0B63,0x2B6B,0x2C6B,0x2B6B,0x4C6B,0x2C6B,0x8B6B,0x0C74,0x8C95,0x8CCF,0xCDE7,0xCCF7,0xCCFF,0xEBFF,0xEBFF,0xCBFF,0xCBFF,0xECFF,0xCCFF,0xCDFF,0xEFFF,0xD4DF,0x38B7,0x58AE,0x9495,0xB68C,0x7D7C,0x3F95,0x5FB6,0xDFBE,0x7FAE,0x9F9D,0xDE84,0xBE84,0xFF8C,0xBFAD,0x7FBE,0xFFC6,0xDFB6,0xBC8D,0xF663,0x786B,0x7663,0x587C,0x5063,0x8D4A,0x6F52,0xB14A,0xF352,0xB35A,0xF45A,0x3863,0x5EA5,0x9FBE,0xBF9D,0x1F95,0x9FC6,0x1FC7,0xBFB6,0xBE9D,0xBB8C,0xDA73,0x986B,0x986B,0x966B,0x555B,0xB352,0xFB5A,0x9F8C,0x5FB6,0x9FAE,0x1C7D,0x9C5B,0x5B53,0xDA42,0x9842,0x793A,0xBC4A,0x9E6B,0xFF8C,0xFF7C,0x7C4B,0x993A,0xDA42,0x9B53,0x7732,0x5932,0xDB42,0xBD73,0x9FBE,0xBE9D,0xFE84,0x9FBE,0x5F85,0xFF9C,0x9FBE,0x9FAE,0xDFA5,0xDE9D,0xDE8C,0x1F8D,0x3F8D,0x5F9D,0x7FBE,0x5FB6,0xFFCE,0x1FCF,0x1FC7,0xB484,0xAA5A,0xA95A,0x895A,0x8952,0x6952,0x4852,0x4852,0x484A,0x284A,0x284A,0x0742,0x0742,0xE741,0xC741,0xC639,0xC639,0xA639,}, +{0x0B6B,0x0B6B,0x2B6B,0x2B6B,0x2C6B,0x2C6B,0x4C6B,0x4C73,0xEB7B,0xCB94,0x6B9D,0xCBB6,0xECE7,0xCCFF,0xCCFF,0xCBFF,0xCAFF,0xCAFF,0xCBFF,0xCEE7,0xD4DF,0xD3EF,0xCFFF,0xD0F7,0x76D7,0x7BBE,0x9C9D,0xD88C,0x5874,0x5D74,0xFF8C,0x9F9D,0x7FBE,0x7FB6,0x9F95,0xBD7C,0xFB6B,0xFC6B,0x3E7C,0x1F95,0xFFAD,0x1FAE,0xDFAD,0x7B6C,0x765B,0x946B,0x125B,0x716B,0x5263,0xF052,0xB04A,0x3263,0xD273,0xAD5A,0x314A,0x1B84,0xFFB5,0x7F74,0x1C5B,0x7FA5,0x5FB6,0x5FB6,0x1D8D,0xB863,0x9863,0xF873,0x756B,0xF352,0x714A,0x113A,0xB952,0xBD73,0x7FA5,0x9FB6,0xFE95,0xBB53,0xF94A,0xB93A,0x1A43,0x793A,0x5932,0x9C42,0xBE5B,0x7D53,0xDA3A,0x983A,0xBA42,0xFA42,0x5A4B,0x973A,0xB94A,0x5C63,0x5E95,0x7E6C,0x5E84,0xBFBE,0xDF9D,0x3F95,0x3FAE,0x5F8D,0x9FC6,0x9E8D,0x9E7C,0x3E74,0xFF8C,0x1FAE,0xFFA5,0xFFAD,0xDFBE,0x3FCF,0x9DB6,0xF073,0x727C,0xAA5A,0x895A,0x8952,0x6952,0x6852,0x4852,0x484A,0x284A,0x284A,0x274A,0x074A,0xE741,0xE741,0xC639,0xC639,}, +{0x0B63,0x2B6B,0x2B6B,0x2B6B,0x2B6B,0x2B6B,0x6C6B,0x4C84,0xCC84,0x6A8D,0x0BC7,0xCCDF,0xCDEF,0xCBFF,0xCAFF,0xC9FF,0xE9FF,0xC9FF,0xC9FF,0xEAF7,0xD3D7,0x7DB7,0x3DBF,0x59D7,0x17D7,0xFAAD,0xFD8C,0x9C84,0xBC84,0x196C,0xDB6B,0x7E7C,0x1F8D,0x9F9D,0xBFA5,0x5F95,0xFC63,0x585B,0x174B,0xF952,0xBD6B,0xFE73,0xFD6B,0xDC6B,0x1B74,0x587C,0x167C,0xD56B,0xB57B,0xB373,0x5263,0xCE52,0x9052,0xF162,0x6E4A,0xEE39,0x7652,0xDA52,0xF74A,0xF529,0x5A63,0x7E84,0x7E7C,0x1A64,0xD66B,0x5563,0x566B,0x5553,0xB24A,0x0D3A,0x6F31,0x1542,0x3C5B,0x5E7C,0x5F95,0x7F95,0x5E6C,0x7A4B,0xB83A,0xFB42,0xD942,0x9A3A,0x7A32,0x5C5B,0xBD95,0x9A6C,0xD842,0x973A,0x3B4B,0x9C53,0x9B53,0xD83A,0x9A42,0xFB4A,0x3D4B,0x5E84,0xFFBE,0xBFAE,0x3F95,0x5F8D,0xBE84,0x1FC7,0x7E8D,0x5E74,0xDD6B,0x7FA5,0x3FAE,0x5F8D,0x9FBE,0xBFC6,0xBFBE,0x7C95,0x5DB6,0x6D5B,0xAA5A,0x895A,0x8952,0x8952,0x6952,0x6852,0x4852,0x484A,0x484A,0x274A,0x0742,0xE641,0xE641,0xE639,}, +{0x0B6B,0x0B63,0x2B6B,0x2B6B,0x2C6B,0x8C73,0xCB84,0xECA5,0xACB6,0x8BC7,0xCBDF,0xCBEF,0xCBF7,0xCBFF,0xCAFF,0xC8FF,0xC8FF,0xE9FF,0xC9FF,0xE9FF,0xEAF7,0xB1DF,0x7DA6,0xBF8D,0xDF6C,0x5F64,0xDF7C,0xDF7C,0xBE74,0x9C7C,0x396C,0x9663,0x9A63,0xDD6B,0x3E6C,0x1E74,0xBD5B,0x1943,0xF64A,0xF54A,0xB54A,0x1653,0xB54A,0xB542,0x523A,0xF65A,0x5C7C,0xDC7C,0x376C,0xF66B,0x9363,0xF15A,0x325B,0xF052,0x904A,0xF25A,0xF452,0x723A,0xD64A,0x985B,0xF34A,0x1132,0x3542,0xD652,0x745B,0x5363,0x7363,0x9363,0x114B,0xD042,0x8B31,0xAF29,0x353A,0x5942,0x1D5B,0xFE73,0x5F7C,0xFEA5,0x7D85,0xFC5B,0xBD53,0xB93A,0x1832,0x382A,0x9D63,0x3FAE,0xFE95,0x5A43,0x793A,0xFB4A,0xFA4A,0x5B53,0xBB53,0xD742,0x973A,0xB842,0xFE94,0xDFBE,0x7FAE,0x1F8D,0xFD63,0x1FB6,0xFFBE,0xFE84,0x9E74,0xFD6B,0x9F9D,0xBF84,0xFF94,0x5FAE,0x7FB6,0x3FAE,0xBFCE,0xF995,0xEA62,0xAA5A,0xAA5A,0x8952,0x8952,0x6952,0x6952,0x6852,0x484A,0x474A,0x284A,0x0742,0x0742,0x0642,}, +{0x2B6B,0x0B6B,0x2C63,0x2C6B,0x6C6B,0xCB84,0xABB6,0x6CCF,0xCCD7,0xCAE7,0xCAEF,0xCAF7,0xC9FF,0xEAFF,0xC9FF,0xC8FF,0xC8FF,0xC9FF,0xC9FF,0xC9FF,0xEAF7,0xC9F7,0x6EE7,0x5BAE,0x5F85,0x9F64,0x7F7C,0x9F74,0xDF84,0x3F95,0x1F85,0xBE7C,0x5B6C,0xB863,0x184B,0xF842,0xD84A,0xB74A,0xF752,0x9B84,0x7C95,0x5B8D,0x9A74,0x5974,0x9A74,0x9974,0x1964,0x3C6C,0x9D84,0xBE84,0x7C74,0x597C,0xB563,0x946B,0x9573,0x7463,0xD452,0xB54A,0x1753,0xB54A,0xB34A,0x724A,0xD04A,0xB052,0xCF52,0xEF52,0xF052,0x1153,0xD052,0x0D3A,0xCC39,0xEF39,0xF239,0x143A,0xF639,0x783A,0x583A,0x5A42,0x3E9D,0xFFBE,0xDD95,0xDB5B,0x3943,0xB521,0x5832,0x7E63,0x9F9D,0x3FA6,0x1B5C,0x5832,0xBB3A,0x9A3A,0x5C53,0x5D74,0x984B,0x7542,0x3953,0x7EA5,0x5FAE,0x3E85,0xDD5B,0x3C7C,0x7FB6,0x1E9E,0x1FAE,0x1D85,0x7E84,0x5D7C,0x7E74,0x9FA5,0x7F9D,0xDFA5,0x5FBE,0xDFBE,0xB47C,0xCA62,0xCA5A,0xA95A,0x8952,0x8952,0x6952,0x684A,0x4852,0x484A,0x274A,0x274A,0x0742,0x0742,}, +{0x0B6B,0x2C6B,0x2B6B,0x8B6B,0xAB84,0x6BAE,0x8AC7,0xCADF,0xCAEF,0xC9EF,0xC9EF,0xC8EF,0xC8FF,0xC9FF,0xE8FF,0xC8FF,0xC8FF,0xCAFF,0xEBFF,0xCBF7,0xCDEF,0xCEE7,0x6CE7,0x2FCE,0x5685,0x5B85,0x3C54,0x7D53,0x1F6C,0x3F6C,0x1F6C,0x1F6C,0xDD63,0xB963,0x565B,0x9442,0x513A,0x9142,0x1453,0x9A5B,0x3E7C,0x3F9D,0xDFAD,0x3FB6,0xDFC6,0x7FD7,0x7FD7,0xBEB6,0x1E9E,0x3E85,0x1B64,0x196C,0xD963,0x1553,0x345B,0x1453,0x5242,0x944A,0x944A,0x7242,0x0F42,0xCD39,0xED41,0x6F4A,0x4E3A,0xEE4A,0xAC4A,0x4C42,0x2C4A,0x2C42,0xEC31,0x6B29,0x0C21,0x7129,0x122A,0x333A,0x3232,0x773A,0x1A4B,0x5B6B,0x3FB6,0x1FBF,0x3D85,0xF942,0x5732,0x592A,0xDC52,0x1F8D,0x5FA6,0xDD6C,0x783A,0xB942,0x3732,0x783A,0xDC63,0x5A64,0xBE9D,0x3E7D,0x9F9D,0x9F95,0x7A4B,0x9C63,0x7F9D,0x3FAE,0xFE8C,0x9FAE,0x9D7C,0x5C6C,0x9C5B,0x5E74,0x9F84,0xDF84,0x1FB6,0xDFC6,0x5DA6,0x6D63,0xEA5A,0xAA5A,0x8962,0xA95A,0x8952,0x6952,0x6852,0x6852,0x2852,0x284A,0x074A,0x0742,}, +{0x0B6B,0x2B6B,0xCB6B,0x4B8D,0xCAAE,0xAAD7,0xCAE7,0xCAEF,0xCAEF,0xCAF7,0xC8F7,0xC8F7,0xC8FF,0xC8FF,0xC8FF,0xC9FF,0xE9FF,0xCAFF,0xCCFF,0xCEFF,0xD4E7,0x58CF,0xD8BE,0xD5AD,0x9384,0x786C,0xBA6C,0xF75B,0x5853,0x7B63,0x1B53,0xFB4A,0xD83A,0xD74A,0xD44A,0x724A,0xF152,0x976B,0x3D84,0xDC6B,0xDD63,0x5C5B,0xBA4A,0x5D63,0x3E7C,0xDF84,0x7F9D,0xDFAD,0x5FB6,0x3FAE,0x3F85,0xD95B,0xF54A,0x3553,0x1453,0xF452,0x303A,0x0F3A,0xEF39,0x103A,0x914A,0x0E42,0xD04A,0xB252,0x7663,0x9563,0x315B,0xCE52,0x0B42,0x2F4A,0xB44A,0x943A,0x943A,0x333A,0x3132,0xAE29,0x4E21,0x6F21,0xF118,0xB95B,0x5B6C,0x7D7C,0x5FB6,0xDD95,0x7B4B,0x582A,0x172A,0x7C42,0x5F9D,0x3FC7,0xDD6C,0x583A,0x5632,0x763A,0x163A,0xFB4A,0xBE7C,0x3FAE,0x5F9E,0x3E85,0x5D6C,0xBA42,0x1E6C,0x5E7C,0xFF84,0xDFA5,0x5E95,0xFB6B,0x5974,0x5A74,0x9E7C,0xDE63,0x5F9D,0x9FBE,0xDFBE,0xD784,0xEA62,0xEA62,0xCA5A,0xA95A,0x895A,0x895A,0x8952,0x4852,0x4952,0x484A,0x284A,0x284A,}, +{0x2B6B,0xCB73,0x4A8D,0x0ABF,0xCADF,0xC9E7,0xC9EF,0xCAF7,0xCBFF,0xCAF7,0xE9FF,0xE8F7,0xE8FF,0xC8FF,0xC8FF,0xC9FF,0xCAFF,0xCBFF,0xCDF7,0xD0F7,0xB2E7,0xB9C6,0xFD9D,0x7D8D,0xB97C,0xB45B,0x5B74,0xD85B,0x3443,0xF35A,0xF552,0xD44A,0xB442,0xD352,0x135B,0xD14A,0x6E42,0x4E4A,0x534A,0xB652,0xB752,0x774A,0x7742,0x5532,0x152A,0xF731,0xBB63,0x1FAE,0x9FBE,0xBFB6,0x9FAE,0x3FA6,0x1EA6,0x7D8D,0x396C,0xF663,0xB453,0xB142,0x4E3A,0xD252,0x9CA5,0xBB95,0x7A74,0x996B,0x775B,0xF77B,0x5B7C,0x1D95,0xBB9D,0x976C,0xB34A,0xB64A,0x5742,0x9842,0xD742,0x5642,0x1232,0xCD29,0x2A19,0xEA18,0x3229,0x1C64,0x7E7C,0xBF84,0x5F64,0x5C4B,0x7732,0x7A32,0x7A42,0x1E95,0x7F8D,0xDC53,0x983A,0x963A,0x3432,0xD229,0x1629,0x3F74,0x5FAE,0x5F96,0xFA63,0xFB6B,0x5B53,0xFB4A,0x9C63,0xDC73,0x3F95,0xDC7C,0xDEAD,0x3B85,0xB96B,0xDD5B,0x1E7C,0xDFAD,0xBFB6,0xFEAD,0xCE6B,0xEA62,0xCA5A,0xC962,0xA95A,0x895A,0x8852,0x8952,0x6852,0x684A,0x484A,0x284A,}, +{0x8B6B,0x0B8D,0x0ABF,0xC9DF,0xC9E7,0xCAEF,0xC9F7,0xC9F7,0xCBFF,0xCBF7,0xCAFF,0xEAFF,0xCAFF,0xCBFF,0xCAFF,0xCBFF,0xECFF,0xCDFF,0xCEF7,0x90CF,0x71B6,0x3495,0xD974,0x1E7D,0x5F95,0xDB84,0xF753,0xF963,0x965B,0x324B,0x3253,0x1353,0x914A,0xD152,0x3253,0x7363,0x5563,0xF352,0x9342,0x0F3A,0xAD31,0x0C21,0x2D21,0x7029,0x9229,0x9431,0xB429,0x5A6B,0x9D8C,0x9FAD,0x7FBE,0xDFCE,0xDFC6,0x7FBE,0xFF9D,0xBFBE,0x9FB6,0xDA7C,0xB342,0xD029,0x1432,0x9D94,0xDFC6,0x5EA6,0x3D85,0xBA63,0x7963,0x1A63,0xFE94,0x5FAE,0x7FAE,0xDB74,0x9742,0x5742,0x3532,0xD231,0xD239,0xDBAD,0xFC9D,0xD995,0x355C,0x9221,0xD852,0x1D4B,0x9D5B,0x3C4B,0xDC3A,0xFA3A,0x372A,0x3A32,0x7E5B,0xBF53,0xBA3A,0xB742,0x973A,0x943A,0xD221,0x9521,0x5E63,0x5FAE,0x1E8E,0x1753,0xFA6B,0x184B,0x9C5B,0x7963,0x9D8C,0x9F95,0x7F9D,0xBFB6,0x7A6C,0xBD63,0xDE5B,0x1E95,0x9FBE,0xFFA5,0x5895,0xEA62,0xCA62,0xC95A,0xA962,0xA95A,0xA952,0x8952,0x8952,0x884A,0x484A,0x484A,}, +{0xCB84,0xCBBE,0xC9D7,0xCAE7,0xCAE7,0xC9EF,0xC9F7,0xC9F7,0xE9FF,0xCAFF,0xEAFF,0xCAFF,0xCAFF,0xCBFF,0xCCFF,0xCCFF,0xCCF7,0xCEF7,0xCFEF,0x51CF,0x968D,0xB764,0x585C,0x5A64,0xFE7C,0x3F8D,0x9D7C,0x3A6C,0x1664,0x366C,0xD363,0x7163,0x105B,0x315B,0x9463,0x5463,0x7663,0xF552,0x7342,0x333A,0xEF31,0x4B19,0x2819,0x0821,0x2D19,0x2E19,0xEB10,0xC908,0xED10,0xB410,0x5929,0x1C6B,0x3E9D,0x1FAE,0xFF9D,0x3FAE,0x1EAE,0xFD84,0xF963,0xF442,0xF54A,0x5532,0x3952,0xDC94,0xBFC6,0x9FAE,0x9D7C,0xD84A,0xD931,0x7C6B,0x1F9D,0xFFA5,0x9E95,0xB953,0x3432,0xD029,0x6B19,0x2C29,0x1642,0x9E8C,0x3FAE,0x7EA6,0xDA74,0xB632,0xF829,0x7A3A,0x3D6C,0xFB42,0xBA3A,0xF821,0xF829,0x3C32,0x172A,0x353A,0xB642,0x594B,0xD63A,0xB221,0x5421,0xBE6B,0x5F9E,0x7C75,0x5953,0x7B5B,0xD952,0x3C53,0xFB52,0xFF8C,0xFF8C,0x7FBE,0x5F9E,0xDB5B,0xBE8C,0x3FAE,0x5FB6,0xDFA5,0x1DB6,0x4B63,0xEA62,0xCA62,0xC962,0xC95A,0xA95A,0x8952,0xA952,0x8952,0x6852,0x484A,}, +{0x8A95,0x29B7,0xC9D7,0xC9EF,0xC9EF,0xC9F7,0xC9F7,0xE9FF,0xC9FF,0xE9FF,0xEAFF,0xCAFF,0xCBFF,0xCAFF,0xCBFF,0xCCFF,0xCCF7,0xCCEF,0xCDE7,0x10C7,0x7B85,0xDF74,0xDF84,0x1F7D,0x3F8D,0xFF7C,0xBD74,0x9B6C,0x7B74,0x5774,0xF56B,0xB46B,0xF473,0x1974,0x9B7C,0xDB84,0x5B6C,0x995B,0x174B,0x743A,0xF039,0x6E29,0x8B21,0x6921,0x4C21,0x0E21,0x2E21,0x6E21,0x4E21,0xB021,0xB019,0x8F19,0xD731,0x9E6B,0x1F9D,0x1FAE,0x9FB6,0x9FB6,0xDF9D,0x9C74,0x5753,0x943A,0x7442,0xF74A,0x3642,0x1B9D,0xBE9D,0x3D85,0xBC74,0xF842,0x9829,0x3B63,0x7E7C,0xDF7C,0x9A53,0x5432,0xF029,0x6C21,0x8F21,0xF739,0xBC52,0x5F7C,0xFFAD,0x7FA6,0xDD6C,0xDA42,0xBB3A,0x5932,0x5A3A,0x7932,0xF729,0xB421,0xD321,0x8F19,0x5232,0x1332,0x5732,0x3622,0xD521,0x1932,0x9E63,0x9F8D,0x9A43,0x3D4B,0x5953,0x7A53,0x1D43,0xDC52,0x5F6C,0x1E95,0x9FB6,0xBE74,0x5C63,0x3FB6,0x1FAE,0xDFA5,0x3FBE,0xEF6B,0x0A5B,0xEA62,0xEA5A,0xCA62,0xCA5A,0xA952,0x895A,0x8952,0x6852,0x4852,}, +{0x0BBF,0xC9D7,0xC9E7,0xC9E7,0xC8E7,0xC8F7,0xC9F7,0xC8FF,0xC8FF,0xC9FF,0xC9FF,0xEAFF,0xCBF7,0xCAF7,0xCAEF,0xCADF,0xEAE7,0xCBDF,0xACC7,0x2D96,0x547D,0x7B6C,0x1C5C,0x9F7C,0xFF8C,0x3F8D,0x7F95,0x3E85,0x9D74,0xF763,0x396C,0x5A7C,0xDD84,0x5E95,0x1E8D,0xFE8C,0xFE8C,0xDF7C,0xBE74,0x3C6C,0xF742,0x112A,0xAD21,0x6A29,0x4B21,0x0A19,0xEA18,0xEB18,0x2C21,0x7021,0x9219,0xD329,0xB331,0x7529,0x193A,0xDB52,0x7D63,0xBD73,0x3D7C,0xFC6B,0x3753,0x724A,0x2F3A,0x3142,0xF44A,0xB342,0xFA52,0x9F9D,0x7FAE,0x9F8D,0x7953,0x563A,0x7521,0x793A,0x3942,0x7B6C,0x796C,0x3843,0x112A,0x9529,0x5832,0xDC4A,0x3E5B,0x7F7C,0xBF9D,0x5F8D,0x3B4B,0x5932,0x793A,0xF842,0xB63A,0x9219,0xF129,0xEF21,0xAC19,0xCE21,0x9221,0x3422,0xD421,0x1722,0x1B43,0x7E5B,0x3E54,0x593A,0xDC42,0xDE9D,0xBB4B,0x9B42,0x7D5B,0x9C63,0x3FAE,0x7E8D,0xDD6B,0x3E9D,0x5FB6,0xBF9D,0x9F9D,0x388D,0x2A63,0x0A63,0xEA62,0xEA62,0xCA5A,0xC952,0xC95A,0xA952,0x8952,0x6952,}, +{0x4ABF,0xE8D7,0xC9E7,0xC9EF,0xC9F7,0xC9F7,0xC9F7,0xC9FF,0xE9FF,0xC9FF,0xEAFF,0xEAFF,0xCAFF,0xC9F7,0xC9E7,0xC9CF,0xC9CF,0xCAC7,0x4DA7,0x0E7E,0xAE64,0x335C,0xB453,0x9B53,0xBD63,0xFE63,0x5F74,0x5F6C,0x9C63,0x9B84,0x7EA5,0xBF9D,0xFEA5,0x1FA6,0x5F6C,0x1F6C,0x5F7C,0xDF84,0xDF8C,0x5F74,0x5C4B,0x753A,0x102A,0xCD21,0x2A19,0x758C,0x387C,0x3B74,0x3D74,0x3D4B,0xDA42,0x3632,0xB121,0xD121,0xB021,0x9019,0xB131,0x343A,0x7442,0x523A,0x103A,0x8F4A,0x4E3A,0x6F42,0xD242,0xF54A,0xD752,0x994A,0xBD6B,0x3F95,0x5F95,0x9D74,0x3EA6,0xDD95,0x586C,0xF539,0xFC8C,0x3FA6,0x1D7D,0x183B,0xD521,0xD529,0xD629,0xDA31,0xFE52,0x1E6C,0x9E53,0xFB5B,0x783A,0x583A,0x7C4B,0x173B,0x132A,0x122A,0xCF19,0x4C19,0x4E19,0x7219,0xB419,0x782A,0xD931,0x9D53,0x5D43,0xB73A,0x5732,0xBE73,0xDF95,0x3A4B,0x9B5B,0x3B4B,0x3E74,0x9F8D,0x1B4B,0x3E74,0x5FAE,0x7F95,0x9F9D,0x3FBE,0xB37C,0x2B63,0x0B63,0x0A63,0xEA62,0xEA5A,0xC95A,0xA95A,0x8952,0x8852,}, +{0x2ABF,0xC8CF,0xE9DF,0xC9E7,0xC9EF,0xE9F7,0xCAF7,0xCAF7,0xC9FF,0xEAFF,0xEAFF,0xCBFF,0xE9F7,0xC9EF,0xC8E7,0xC6D7,0x86C7,0x29AF,0x8C8E,0x8D5D,0xCE4C,0x0F4C,0x2F3B,0x9232,0x5332,0x374B,0xFE5B,0xBE5B,0x9B53,0x584B,0xF94A,0xBC6B,0xFF8C,0xFEB5,0x7FA5,0xDE5B,0x7B3A,0x1A32,0x3B3A,0x3A32,0xD731,0x1332,0xEF29,0x8C21,0x6B21,0x4C21,0xC910,0xCF10,0xB218,0xF318,0x5519,0xB429,0x3232,0x3342,0x553A,0xF531,0xD429,0x5542,0x143A,0x3132,0x4E42,0x6E4A,0x4D42,0x134B,0xF652,0x7442,0x7642,0xB74A,0x773A,0x383A,0x7D84,0x9F95,0xBD5B,0x1E95,0x7FB6,0x9EAE,0xFA7C,0x5963,0x9E9D,0xBF95,0x7C64,0xD932,0x5932,0x592A,0xB721,0x3A3A,0x1A2A,0xB721,0xDB63,0x7B64,0x792A,0x9A3A,0x1C5C,0x1964,0xD53A,0x122A,0xD021,0x3011,0x5219,0xF829,0xB619,0x3B43,0xFA32,0x522A,0xEF21,0x573A,0xBE84,0x7C64,0x3A4B,0xFB4A,0x9C42,0x9F7C,0x7B53,0x7E5B,0x3E9D,0x5E8D,0x1F95,0xBFA5,0x5DB6,0xED6B,0x0B63,0x0A63,0x0A5B,0xEA62,0xE95A,0xC95A,0xA952,0xA952,}, +{0xA9B6,0xA8C7,0xE8DF,0xC8E7,0xC8E7,0xC8EF,0xC9F7,0xEBF7,0xCCF7,0xEBFF,0xCAFF,0xCAFF,0xCAF7,0xCAEF,0xC7E7,0xC5DF,0x87CF,0x2AA7,0x6C86,0xAD65,0xCD5C,0x6A3B,0x0C3B,0x6E32,0x2F32,0xBB6B,0xDF84,0xFF84,0xBF7C,0x3D6C,0x9B5B,0x3753,0x974A,0x973A,0x784A,0xBA42,0x7942,0x783A,0x573A,0xF329,0x323A,0x1332,0x333A,0x523A,0x7142,0xED29,0xA931,0xCA31,0x8E31,0x7121,0x9229,0x4F21,0x4D21,0xED18,0x4F09,0x5019,0x7219,0xF429,0x763A,0x3332,0x0E3A,0x0C3A,0xEC39,0xAC31,0x0F42,0x1242,0x9542,0xB442,0x9342,0xF331,0xB729,0x1E74,0xFF84,0x9E7C,0xFE6B,0x9E7C,0x9E7C,0x9E74,0x9932,0x9B63,0x1E85,0x5E64,0x9832,0x172A,0x382A,0xD629,0xD419,0xB219,0xD731,0xBE84,0xBE74,0xF93A,0x783A,0xDF84,0xDF95,0xDA74,0x502A,0x1332,0x5319,0xF310,0x171A,0xB429,0xFA3A,0x7632,0xF021,0xAD08,0xFB4A,0xFF84,0xDD53,0x7D53,0x7732,0x3C53,0xBB5B,0x783A,0x5B5B,0x5D7C,0x1C74,0x1F95,0x1FB6,0x978D,0x2A63,0x2A6B,0x0A5B,0xEB62,0x0A5B,0xE95A,0xC95A,0xA95A,}, +{0xC9A6,0xC8CF,0xC7DF,0xE8DF,0xC9E7,0xCAEF,0xC9F7,0xCAF7,0xCBF7,0xCCF7,0xEBF7,0xCAF7,0xCAF7,0xEAEF,0xC8E7,0xC7DF,0xC7D7,0x4AAF,0x4C86,0x8D65,0xAD54,0xCC53,0xEB3A,0xCD32,0x4E32,0xD542,0x3D53,0x9E63,0x1F74,0x9F84,0x9F84,0xBA5B,0x9442,0x3342,0x3142,0x9342,0x753A,0x3532,0x1632,0x5542,0xD84A,0xD642,0xF54A,0x9442,0x9242,0x6F42,0xB052,0x5463,0x1563,0x323A,0xB031,0x983A,0x1C64,0x9D74,0x7D74,0x3B64,0x1843,0xB621,0x1932,0x7732,0xD029,0xAC29,0xAB39,0x6B4A,0x9152,0x5B5B,0xF742,0x143A,0xB742,0x963A,0x142A,0x9521,0x794A,0xDF7C,0x7E6C,0x7C5B,0x7D4B,0xBC3A,0xFC42,0xB93A,0x1832,0xDB42,0x9B3A,0x572A,0xF421,0xD421,0xD321,0xB021,0x9121,0xF929,0x7D63,0x5F85,0x7B4B,0x9731,0xFE73,0xBF9D,0x5D85,0x7743,0xD329,0x9419,0x1211,0xB019,0x7632,0x152A,0x5221,0x122A,0x1019,0x1D6C,0xFE7C,0x9E74,0x3643,0x9B53,0xF64A,0x703A,0x3553,0x9563,0x996B,0xDF84,0x5FA5,0xDC9D,0x6B6B,0x2B63,0x2B63,0x0A63,0xEA62,0xEA62,0xCA62,0xC95A,}, +{0x499E,0x66B7,0xC7CF,0xC8DF,0xC9DF,0xCAEF,0xCBEF,0xCAF7,0xC9F7,0xC9EF,0xE9F7,0xCAF7,0xCAF7,0xC9EF,0xC8E7,0xC7DF,0xC8C7,0xE996,0x0A76,0x6B5D,0xAA4C,0x0B44,0x6C43,0x4F43,0xF13A,0xB23A,0x3332,0x153A,0x383A,0x7B3A,0x9B4A,0x7842,0xF229,0x3132,0x144B,0x1443,0x3743,0xB642,0x322A,0xB432,0x5A53,0xBA5B,0x9A53,0xB95B,0x164B,0x5032,0x4E3A,0x0E3A,0x6D31,0x2A29,0x0B21,0xAF20,0x3621,0x3B3A,0x9C4A,0x9B3A,0x3932,0x7211,0xD208,0x3519,0x9121,0xCF21,0xD242,0xB53A,0x542A,0x1C4B,0xDE63,0xBE7C,0x9953,0x1632,0xB429,0xF329,0xB229,0x9829,0x3D5B,0xFE5B,0x7732,0xD421,0x9421,0x7421,0xF629,0x362A,0x593A,0x9A3A,0x7932,0x5732,0xD421,0xB021,0x8F21,0x5219,0x5A3A,0x9E5B,0x7F6C,0x7B43,0x1319,0x9B63,0x3F8D,0x1F7D,0x3C5C,0x962A,0xED08,0xAF21,0x8F21,0x353A,0xD329,0x343A,0x382A,0x182A,0xBE63,0x5F6C,0x5C7D,0x3A4B,0x543A,0xA931,0x2E42,0xF052,0x746B,0x3D74,0x5E7C,0xBE9D,0xAE63,0x4A63,0x2B6B,0x2A63,0x0A63,0x0A63,0xEA5A,0xCA5A,}, +{0xA97D,0xE796,0xC8B7,0xC8BF,0xC9D7,0xCADF,0xCBE7,0xCAEF,0xC9EF,0xEAEF,0xC9F7,0xE9F7,0xC9F7,0xC8F7,0xC9E7,0xC9D7,0xA8B7,0xE896,0x4A7E,0x6A5D,0xAB4C,0x4D4C,0xCF4B,0x345C,0x9553,0xD242,0x713A,0xF029,0x6F21,0x5019,0x3021,0x4F19,0x2D19,0xCD31,0xDB7B,0xDF8C,0xFF8C,0xDF84,0x1E95,0x9F9D,0x9F9D,0xBF9D,0x7F9D,0x3F8D,0xDC63,0xD542,0xD44A,0x313A,0x6D29,0x4929,0x4721,0x0521,0xEB20,0xD010,0xF310,0x3411,0x9419,0x4F11,0x4E19,0x6F19,0x2C11,0x2D19,0x7B63,0x3F95,0x3F8D,0x7C53,0x372A,0xFC42,0xFE63,0x3A4B,0xF229,0xB121,0x553A,0xF729,0xD729,0x5B3A,0x9832,0x3632,0x9329,0x2D19,0x2A11,0xEE10,0x9321,0x9621,0xD819,0xD821,0xF821,0x1422,0x3132,0x0F2A,0x9219,0xF831,0x9D4A,0xBF5B,0x1A3B,0xCD10,0x7531,0xDD63,0xBF74,0xDC4B,0x0F22,0xA818,0x2B2A,0x103A,0x533A,0x4F11,0x3119,0xD521,0xB621,0x1D4B,0xBF95,0x9A5C,0x985B,0xAC31,0xA931,0x2C42,0x8F52,0xD96B,0x5D6C,0xBF9D,0x7474,0x4B6B,0x4B6B,0x4B6B,0x2B63,0x0A63,0x0A63,0xE95A,}, +{0xE886,0xA9A7,0xC9C7,0xCAD7,0xCADF,0xCAE7,0xEAEF,0xCAF7,0xC9EF,0xCAEF,0xCAE7,0xCBEF,0xCCEF,0xCCEF,0xCCE7,0xCCDF,0xCCCF,0xCCB7,0xB2B7,0x8D86,0x3165,0xB35C,0x544C,0xBA64,0x1854,0x3443,0x913A,0xAE21,0x0C19,0xEC10,0xEC10,0xCB10,0xA908,0xA610,0xEA18,0x354A,0xDC42,0xFE4A,0xFE52,0x3E5B,0x3D53,0xFD52,0xFC52,0xDC4A,0x393A,0xD531,0xD331,0x9129,0x6E29,0x8A21,0xA929,0x6621,0x6929,0xB221,0xB619,0x9519,0x772A,0xF932,0xDA3A,0x372A,0xF221,0x1753,0xD93A,0xFB21,0xBC4A,0x393A,0xF629,0x5932,0x7A3A,0xDA42,0x573A,0xB33A,0x3332,0xF429,0xD429,0x7221,0x0F19,0x1119,0x9321,0x342A,0x122A,0xAE19,0x2D19,0x5121,0xD621,0x582A,0xD721,0x362A,0x2E21,0xB129,0xD021,0x8F21,0x9529,0x7C3A,0x7B32,0x1222,0xAF21,0xB119,0x3742,0xFA3A,0xB021,0x092A,0xEA31,0xED31,0x0F32,0x2E19,0x1211,0x2F11,0xD221,0x192A,0x3F74,0x1E8E,0xDA63,0xCA31,0xE941,0xA839,0x4D42,0x3663,0x7E74,0xDFA5,0xF884,0x4B6B,0x4B6B,0x4B6B,0x2B6B,0x4A63,0x0A63,0x0A5B,}, +{0xEBC7,0xCBD7,0xCBE7,0xCAEF,0xC9EF,0xC9F7,0xCAF7,0xCAF7,0xC9F7,0xCAF7,0xC9EF,0xC9EF,0xCBEF,0xCEEF,0xCDE7,0xEAD7,0xC9BF,0x4BA7,0xCD8E,0xAF75,0x995C,0x5E54,0x7F64,0xFF84,0xDF84,0xBE7C,0xDB53,0x5132,0x2A11,0xE810,0xAA08,0xA810,0x8708,0xA610,0xA708,0xEA10,0x0E11,0x5521,0x9721,0xB729,0x9421,0x5421,0x9421,0xB329,0x574B,0xBA7C,0x9C74,0xDC5B,0x9332,0xEC29,0xEB29,0x3032,0x3A4B,0xDE5B,0x1E64,0x5C4B,0x3D53,0x3F74,0x3F74,0xBB42,0x132A,0xD831,0xBE63,0x7B3A,0x7942,0x3B6C,0x1543,0x993A,0xFD63,0x5A53,0xB942,0x3853,0x5C6C,0x3B4B,0x3632,0xB329,0x152A,0xF329,0xFA63,0x5B6C,0x9B53,0xB83A,0x1432,0xB021,0xD321,0x592A,0x382A,0x162A,0x752A,0x132A,0xB029,0xAC29,0xCC29,0x2D21,0x3021,0x7232,0x122A,0xF329,0xF121,0xEF29,0x8C21,0xA929,0x0A32,0xEA31,0x6B29,0x4C21,0xAA10,0x4B19,0x4D11,0x7319,0x1D4B,0xBF8D,0x7B85,0x5353,0x0F63,0xE939,0xB45A,0x5974,0x3B74,0x5E9D,0x7B85,0x8C6B,0x4B63,0x4B6B,0x2B6B,0x2A63,0x0A63,0x0A63,}, +{0xCBD7,0xCBDF,0xE9E7,0xC9E7,0xE8EF,0xC9F7,0xCAF7,0xCAF7,0xCAF7,0xCAF7,0xC9F7,0xC9F7,0xC8EF,0xCBEF,0xCBE7,0xC9D7,0x88C7,0x6796,0x2975,0x2C4C,0xD143,0xB743,0x5B43,0x3C53,0x5D53,0xFB4A,0xDC4A,0x363A,0xD021,0x6E21,0x0D11,0xCA00,0xAA00,0x8B00,0xED08,0x2F21,0x2C11,0xCB10,0x8908,0xAA08,0xCB08,0x2E11,0xF229,0xFA4A,0xFE73,0x3E74,0x7E63,0x7D3A,0xD721,0x9519,0x5119,0xEF20,0x5829,0xBB29,0x3B21,0xD708,0x9008,0x5000,0x2E08,0x6C08,0xCA08,0x2B11,0xF318,0x1319,0x3319,0x3C42,0x7E74,0x1F64,0x5F74,0xDF7C,0xFB63,0x783A,0x9B63,0x3F85,0xDD53,0xFB3A,0x3B43,0x7C4B,0xFC4A,0x3E7C,0xFF8C,0x5F85,0x9E6C,0x1943,0xB029,0x9221,0xF731,0xD729,0xD529,0x9219,0x7021,0xAD29,0xF231,0x953A,0xD43A,0x9532,0x3222,0x8E19,0xEC18,0xEB18,0x4921,0x8729,0x8829,0x8831,0x2721,0x4A21,0xC910,0xE818,0x9019,0xF119,0xD829,0xBE5B,0x5C8D,0xB75B,0xB36B,0xB04A,0x9242,0xFC8C,0xFA63,0xBC84,0xBE95,0x767C,0x6B63,0x4B6B,0x4B6B,0x4B6B,0x0A63,0x2A63,}, +{0xCBDF,0xCAE7,0xC9EF,0xC8F7,0xC8F7,0xCAF7,0xE9FF,0xC9F7,0xE9F7,0xC9F7,0xC9F7,0xC9F7,0xC9F7,0xCAF7,0xCAEF,0xCAEF,0xA8D7,0x479E,0xCC64,0xF03B,0xD44B,0xD94B,0x3743,0xF63A,0x352A,0xF421,0xB219,0xAF19,0xF331,0xD129,0xF231,0x112A,0x352A,0xD619,0x1822,0x7742,0x132A,0x2D11,0xC808,0x0B19,0xF531,0xBA42,0x9842,0x362A,0x5419,0xB310,0x9408,0x9208,0x8E00,0x4C00,0x8D08,0x1109,0xF721,0xF829,0xB308,0x8F00,0x4B00,0x8708,0x6708,0xCB10,0x2D19,0x4A21,0x6921,0x4721,0x0B19,0x7721,0x5A19,0xBB21,0x1932,0x3F74,0x9F7C,0xFC5B,0xF631,0x5E7C,0xDFA5,0xDF95,0x7E6C,0x1C43,0xD821,0x7729,0x9A4A,0xDC52,0xFE6B,0x1F64,0xB832,0xF421,0x5119,0xF521,0x1219,0x7119,0xB019,0xF121,0x1522,0x1522,0x4F19,0x0F21,0x572A,0xD73A,0xCF19,0x4F2A,0x2719,0xC420,0x6629,0xE520,0xE520,0xC718,0xCD10,0xE910,0x5021,0x372A,0xB521,0x3B2A,0x9C53,0xF86B,0x3A95,0x796C,0x995B,0x1E8D,0x1D8D,0xBD95,0x1E8D,0x9B7C,0x6C6B,0x4B6B,0x4B6B,0x4A6B,0x2A63,0x2A63,}, +{0xC9E7,0xC9EF,0xC9EF,0xC8F7,0xC9F7,0xC9F7,0xC9F7,0xC9F7,0xCAF7,0xC9F7,0xC9F7,0xC9F7,0xCBF7,0xCBF7,0xCAEF,0xCADF,0x47BF,0xC785,0xCD64,0x9054,0xF343,0x773B,0x5A53,0xD742,0x3532,0x783A,0xB83A,0xF83A,0x3843,0xB642,0x322A,0x1032,0x795B,0xBC5B,0xDB42,0xDB3A,0x7932,0xD321,0x0D11,0xCA10,0xB010,0xD618,0xD610,0xB308,0x6C00,0x6C00,0x4C00,0x6D00,0x6B08,0xCA08,0xB511,0xDA21,0x3A19,0xB808,0x9200,0xAA08,0x8410,0xE410,0xC310,0x2721,0x0721,0x2521,0xE320,0x0321,0x6529,0xEB39,0x5221,0x7621,0x321A,0x172A,0x9D42,0x5E7C,0xBC74,0xBA21,0x7D4A,0x7F7C,0xBF9D,0x1E7D,0xDA32,0x562A,0x3422,0xB219,0xB521,0x9721,0xD629,0x372A,0x5832,0x392A,0xD84A,0xDC7C,0x1E7D,0x3D64,0x1B64,0xFD5B,0x3B3B,0x552A,0x522A,0x7129,0x1543,0xF64A,0xFC74,0x996C,0x7032,0x2521,0xE418,0xA310,0x4B08,0x8808,0x5211,0x3832,0xB519,0x1622,0xB63A,0x7142,0xDC8C,0x9EA6,0xB34A,0x1C7C,0x5F95,0x9FBE,0x3E7D,0xD963,0x6C6B,0x4B6B,0x4B6B,0x2B6B,0x2B63,0x2A63,}, +{0xC8DF,0xC8E7,0xC7EF,0xE8F7,0xC9F7,0xC9F7,0xE9F7,0xC9F7,0xCAF7,0xC9F7,0xE9F7,0xCAF7,0xCAF7,0xEAEF,0xCAEF,0xC9E7,0xA8D7,0x2696,0x695C,0xAA33,0x2B23,0x0F33,0xD43A,0xB43A,0x1422,0x5B3A,0x9E53,0x1F6C,0xFE63,0x7C53,0xD742,0x5132,0x312A,0x1D74,0x1A6C,0xFC63,0x9D53,0xD942,0xF421,0x9211,0xEE08,0x8C08,0x8C00,0x6F11,0xD211,0xD211,0xED00,0xC800,0xC808,0x3011,0x7B3A,0xF510,0x4D00,0x4900,0x2700,0x4208,0xA210,0x8921,0x8829,0xE839,0x2942,0xAD4A,0xED52,0xCC52,0x2E5B,0x4A42,0xC931,0x1019,0xD119,0x0D2A,0xB521,0x383A,0x1F8D,0x1D5C,0xBB3A,0xFD42,0x3E5B,0xDE84,0x1F7D,0x1E5C,0x3D43,0xBA3A,0x993A,0xDA3A,0x5732,0x5832,0x5832,0x582A,0x9521,0x5B3A,0xBF63,0xDF84,0xBF84,0x7F95,0xDF9D,0x1F96,0x5E85,0x1D75,0x764B,0x734B,0x8F29,0x373A,0x5D6C,0x9B64,0x6E2A,0x6621,0xAA10,0x4D32,0xF118,0x1822,0x5B43,0x1C4C,0xF221,0x113A,0x5963,0x9FAE,0x956C,0x1774,0x1C74,0xDFAD,0x7F8D,0xDA74,0xF884,0x0F6C,0x6B6B,0x4B6B,0x6A63,0x4A63,}, +{0xE8E7,0xC8E7,0xC8EF,0xC8F7,0xC9F7,0xC8F7,0xC9F7,0xCAF7,0xC9F7,0xCAF7,0xCAF7,0xEAF7,0xCAF7,0xCAEF,0xCAEF,0xC8E7,0x88C7,0x2796,0x898E,0x2976,0x2B4D,0xAE2B,0xB02A,0x713A,0x313A,0x763A,0x7B3A,0xDC4A,0x3D5B,0x3D53,0xD94A,0xB44A,0x0E22,0x322A,0xFE73,0x1B6C,0xDF8C,0x1E64,0x1C4B,0x7A32,0xB529,0x4E09,0x2B19,0x9942,0x3E5B,0x7C63,0x7719,0xEC10,0xF308,0xF308,0x6D00,0x2C00,0x4A00,0x4408,0x8208,0x2419,0x2521,0x6729,0x4729,0x6639,0xC428,0x4428,0x6330,0xE630,0x0C4A,0xCE5A,0x6A42,0x4729,0x0D19,0xE810,0xC810,0x1011,0x7B42,0x7F53,0x3B22,0x5B2A,0xBD32,0xDB29,0xDD4A,0x3F6C,0x1F64,0x3D6C,0xFE7C,0x5F85,0x7F8D,0x9E85,0x3E7D,0x1C64,0x9832,0x7111,0x1519,0x7B3A,0x3E4B,0x3F64,0xDF5B,0xDB4A,0x3B5B,0xFD63,0x1C5C,0xB65B,0x2D32,0x0A11,0x133A,0xBB63,0xDB5B,0x144B,0xB96C,0x3432,0x9219,0x7721,0xFE4A,0x3F75,0xD82A,0x7432,0x183A,0xDFAD,0xD97C,0x1B74,0x9B7C,0xBD8C,0x5F8D,0x3FA6,0xFFD6,0x98B6,0x6B6B,0x4B6B,0x6B6B,0x4A63,}, +{0xC7DF,0xC7E7,0xC8EF,0xC8EF,0xC8F7,0xC9F7,0xC9F7,0xC9F7,0xE8F7,0xCAFF,0xCBF7,0xCAF7,0xCAEF,0xCBEF,0xCAE7,0xC7E7,0xC5DF,0x47BF,0xE79E,0x677E,0x0B76,0xCC54,0x8F3B,0x6E22,0xCA29,0xAE21,0xD431,0x153A,0x3432,0x9519,0x7319,0x6F19,0x1342,0xAE29,0x9221,0xF94A,0x7C5B,0xFF8C,0x7E74,0xBB3A,0x332A,0xFA73,0xD75B,0xD119,0x1211,0xD208,0x9008,0xCE08,0xB308,0x9300,0x8C00,0x8608,0x8308,0xC310,0x0419,0xE931,0xA731,0xA729,0xE420,0x4048,0x2088,0x4088,0xCC8A,0x1194,0x4862,0x4338,0x4829,0xE839,0x2521,0x0521,0x0519,0xC710,0x7121,0xD921,0x9511,0x7619,0xFB21,0xB819,0x9719,0x9821,0x1A2A,0x3A32,0x9E4A,0xBF63,0xFF6B,0x3F74,0x7F95,0xBF9D,0x1F7D,0x3B5C,0xB32A,0xCB10,0xEF10,0xD218,0x5429,0xF93A,0xB732,0x3853,0x785B,0xB632,0xB119,0x2F11,0x6E19,0xB121,0xB221,0x1432,0x7E84,0x7D8D,0x382A,0xD919,0xFA21,0x9F4B,0xDB32,0xD729,0x9A32,0x1B53,0xBB5B,0x9E84,0x3E85,0x575B,0x7F7C,0x7F95,0x1FB6,0xDDC6,0xCD6B,0x4B6B,0x4B6B,0x6A63,}, +{0xC7E7,0xC8E7,0xE8E7,0xC9F7,0xCAF7,0xC9F7,0xC9F7,0xC8F7,0xE8F7,0xCAF7,0xCAF7,0xC9EF,0xE9E7,0xCBE7,0xE9DF,0xC7DF,0xC6D7,0xA7C7,0xE8AE,0x4875,0x2B54,0xF04B,0xD44B,0x5443,0xAF3A,0x0A1A,0x6A11,0x2D21,0xF239,0x3242,0xF031,0x6C21,0x2A19,0x7021,0x5142,0x1553,0xF842,0x1C4B,0xDF63,0x9D63,0xB842,0x993A,0x9F7C,0x5E6C,0x7832,0x112A,0x1232,0x122A,0xCC00,0xD008,0x7711,0xEC08,0xA410,0x2521,0x6A42,0x8A4A,0x8A4A,0x4B4A,0xA370,0xA0C8,0x80C0,0x6070,0x5694,0x5B9D,0xD894,0x538C,0x0852,0x4731,0x093A,0x8631,0x6629,0xD842,0xF842,0x9D5B,0xFB3A,0xF308,0x1A22,0xB819,0x1311,0x9211,0x6E19,0xB121,0x5419,0x7819,0x9B21,0xFC21,0x7A42,0xFD6B,0x5F74,0xDF7C,0x1F7D,0x7C64,0x333B,0x0B22,0x8719,0x8708,0xA910,0x1243,0xD142,0x7032,0xB321,0x1109,0x9021,0x9011,0xC710,0xAD29,0xBA21,0xFE73,0x7E64,0x1B1A,0xB711,0xD819,0x7311,0x5019,0x5119,0xB221,0xB529,0xBE6B,0x3F85,0x765B,0x7C53,0xBE7C,0xFA5B,0x3EBE,0xB47C,0x6B6B,0x4B6B,0x6B6B,}, +{0xE8DF,0xE8E7,0xC7E7,0xE9EF,0xCAF7,0xC9F7,0xC8F7,0xC7F7,0xC7EF,0xC8F7,0xC8EF,0xC7EF,0xC7EF,0xC9E7,0xC8D7,0x46B7,0xA7A6,0xEA85,0x106D,0xB45B,0x9653,0x7853,0x7B5B,0xBD63,0xDD63,0xF963,0x5BA5,0x735B,0x0C32,0x6A11,0x8C29,0x0C32,0xEB29,0x6A19,0xB031,0x3B5B,0xFF8C,0x5D74,0x1E4B,0xBC42,0x9942,0x352A,0x9C3A,0xDF63,0xDF7C,0xDD5B,0x9C5B,0xFE6B,0xF742,0x142A,0xF729,0xCA08,0xA310,0x093A,0xAC52,0xCD5A,0x8D52,0x6851,0xE0E0,0x00C9,0x4060,0x8218,0xF984,0x1B85,0xD97C,0x5874,0x357C,0xA851,0x8831,0xC739,0x6B42,0xF139,0xDB42,0x1B32,0x7E53,0x7511,0x3211,0x7511,0x5211,0x342A,0xF63A,0x562A,0x172A,0xF721,0xB511,0x7311,0xD619,0x5619,0x3409,0xF418,0x9529,0xD529,0xD029,0xAD29,0x5032,0x7532,0x1543,0x796C,0x594B,0xB832,0x3D43,0x9B2A,0x1609,0xB100,0x0C11,0x4B21,0x3211,0xBB21,0x9C32,0xB819,0x5111,0x4D11,0x0B11,0x8B21,0x0D2A,0x2E11,0x7221,0x1E53,0xBF7C,0xDA63,0x5E74,0xDF9D,0xDA53,0x5D74,0xFCA5,0x8B6B,0x6B6B,0x4B6B,}, +{0xC8DF,0xC7E7,0xC8E7,0xCAEF,0xC9EF,0xC8EF,0xC7EF,0xC7F7,0xC6EF,0xC7F7,0xC7F7,0xC6EF,0xC6EF,0xC5E7,0x45CF,0x63A6,0x457D,0x064C,0x2633,0x8722,0x681A,0x2B22,0x0E22,0xF021,0x132A,0x9432,0x385B,0xD442,0x513A,0x0D3A,0x2E3A,0xF152,0x1A7C,0xD963,0xF442,0x1332,0x7C63,0xDF8C,0xFF84,0xDC5B,0xB83A,0x5432,0x3632,0xBC5B,0x1E85,0x5F8D,0x1F6C,0xDF63,0x1F64,0x3B53,0x1932,0x0B11,0xC831,0x2D5B,0x083A,0x0D5B,0x0A42,0xE3A8,0x23E1,0x2070,0x4118,0xCD4A,0x3B85,0x9A7C,0x3874,0x9142,0xB65B,0x0739,0x6250,0x4C42,0xC831,0xC831,0x6E29,0x5632,0xB921,0x3211,0xF110,0x9C32,0x7211,0x993A,0x5F74,0x3F7D,0xBC53,0x592A,0x5B22,0xF829,0xB719,0xD819,0x9511,0x9111,0xED11,0xEC11,0x4C09,0x6B19,0xAC21,0x0A19,0xA708,0x6510,0x2710,0xF118,0xBA19,0x9C3A,0x7D32,0x9709,0xAB00,0xE810,0xEC10,0x1211,0x5611,0xD000,0x2D09,0x7309,0x5109,0xF731,0xBD53,0xD619,0x1B22,0xB921,0x3E74,0xDE7C,0xFF84,0xBFBE,0xFC7C,0x351A,0x5D74,0xAD6B,0x6B6B,0x4B6B,}, +{0xC8E7,0xC7E7,0xC9E7,0xC9EF,0xC9EF,0xC7EF,0xC7EF,0xC6EF,0xC6EF,0xC8F7,0xC8EF,0xC8EF,0xC7EF,0xC5E7,0x64D7,0x87B6,0x688D,0x695C,0xA933,0x2933,0x4B43,0x0C43,0x8D32,0x303A,0x313A,0x3042,0x1032,0xCD29,0x2D3A,0x5563,0x534B,0xB14A,0x3A5B,0x5F84,0x3E7C,0xF973,0x512A,0x1A53,0xBF8C,0xFF8C,0x1E64,0xB83A,0x3332,0x9742,0x395B,0x7E95,0x5E74,0x7F53,0x3F53,0xBD4A,0x3532,0xAB29,0x4E5B,0x3174,0x2D63,0x0D5B,0x2A4A,0x81D8,0x80A8,0x4118,0xE410,0x5153,0xB55B,0x2F3A,0x6B21,0x2400,0x4A19,0x6308,0x6040,0x4639,0x4A42,0xA731,0x4929,0x3D4B,0x1109,0xC908,0x8908,0x9B19,0xFB19,0xB421,0x5721,0x9C42,0x9D53,0xF842,0x7832,0xD621,0x7511,0xD719,0x9519,0x1422,0x5322,0x0F22,0xCC29,0xCB21,0x2721,0xE618,0x7011,0xEF21,0xEA31,0x2919,0x7210,0xBA19,0x3809,0x7911,0xCE00,0xCB10,0xB321,0x9411,0x3009,0xCA00,0x6B00,0x1909,0x9911,0x9C21,0x3F6C,0xBE42,0xFE32,0x5619,0xBD63,0xFF7C,0x3F8D,0x1E85,0xBF7C,0xFE7C,0x7943,0x4F63,0x4B6B,0x4B6B,}, +{0xC7DF,0xE7E7,0xC9E7,0xC9EF,0xC8EF,0xC7EF,0xC7EF,0xC6EF,0xC6EF,0xC7EF,0xC7EF,0xC7EF,0xE8EF,0xA8E7,0xA7AE,0xA87D,0x885C,0xEB4B,0xCD43,0x8E43,0x0B3B,0xCA3A,0x0D53,0xAE4A,0x4E3A,0xEF39,0x8E29,0x4A21,0xCC31,0x9242,0x7A6B,0xDB7B,0x5853,0xFA4A,0x3B5B,0x7B53,0x5D74,0x774B,0x1D53,0x7F7C,0x7F74,0x3C4B,0x5632,0xD421,0x7411,0xB96B,0xFB52,0x3D32,0x5E3A,0x192A,0x7642,0x0E32,0xAC4A,0x117C,0xCB5A,0x4A42,0x0651,0xA1D8,0x6070,0x8208,0x2519,0x0E3A,0xCB29,0x2400,0x0200,0x0100,0xC410,0xC310,0x8120,0x6058,0xEA39,0x2429,0x8731,0x7129,0xAE08,0xA608,0x2F19,0xB010,0x5711,0x1311,0x9A32,0x3D43,0x1C43,0x392A,0x9521,0x3622,0xF93A,0xB421,0xD521,0xB821,0x1822,0x572A,0xD119,0xD121,0xF73A,0xD632,0xB719,0xD721,0x9221,0x142A,0x3C43,0x1C3B,0x7722,0xEF00,0xEF00,0x8B08,0xFA21,0xFE32,0x5A1A,0xD311,0x9319,0x3922,0x9B11,0x5B19,0xFF4A,0xFE4A,0x5E4B,0x5932,0xFE63,0x9F74,0xBC7C,0x993A,0x5832,0x173A,0xF021,0x0E63,0x6B6B,0x6B6B,}, +{0xC7E7,0xC8E7,0xC8E7,0xC8EF,0xC8EF,0xC7EF,0xC7EF,0xC7EF,0xC8EF,0xC8EF,0xC7EF,0xC7EF,0xC7EF,0xC7E7,0xA7D7,0x07B7,0xC985,0xCA64,0xEA43,0x4C3B,0xAA32,0x8A22,0x0A1A,0xEA21,0xE811,0xC929,0xCA29,0xEA29,0x0B32,0x2E3A,0xD34A,0xDA63,0xFB63,0x1C6C,0xBB5B,0x394B,0x7D5B,0x9F84,0x9D5B,0xFD4A,0x5F53,0xDF63,0x1D43,0xF421,0x5011,0x8F19,0x7719,0x1D53,0x9B3A,0x9621,0x7119,0xAF21,0xEA39,0x2D63,0xEB5A,0xCD5A,0x0561,0x60C8,0x6048,0xC308,0x2521,0x2300,0x0200,0x0100,0x0100,0x0100,0x0100,0x0419,0x6210,0x4058,0x2529,0xC739,0x6629,0x6729,0x0719,0xF408,0x1711,0x1411,0x7519,0xB119,0x3519,0x5B3A,0xFE4A,0x9E4B,0x7A22,0x5309,0x9A53,0xBF74,0x1E54,0x1722,0x182A,0xDD32,0xBE4B,0xDB4B,0xB632,0x172A,0x9B43,0x7A22,0xF711,0x3509,0x3B32,0xDF5B,0x1F54,0xDB32,0xFA32,0xB82A,0x3609,0xBC21,0x3F3B,0xFC19,0xDD32,0x5E54,0x1922,0x5619,0xDA21,0x5D5B,0xFF42,0xFD3A,0xDE53,0xDD42,0xDD7C,0xF842,0x2F19,0xB221,0xEE29,0x2B63,0x6B6B,0x6A6B,}, +{0xC6E7,0xC8EF,0xC9EF,0xE8EF,0xE8EF,0xC7F7,0xC7EF,0xE6EF,0xC8E7,0xC8EF,0xC9EF,0xC9EF,0xE9EF,0xCBE7,0xC9DF,0xA8BF,0x29AF,0x688E,0x885D,0xC83C,0xE72B,0x891B,0x492B,0x4E43,0x156C,0xF763,0xF863,0xFB63,0xB963,0x154B,0x1443,0x184B,0x3E7C,0xBF84,0x3E6C,0xBC5B,0x5A5B,0x1C43,0xBD5B,0x7C53,0xBA42,0xBC32,0x3B32,0x192A,0x4F11,0x6F21,0x0F19,0x7821,0x3E5B,0x7832,0xF429,0x8F21,0x2921,0x8B52,0x2D63,0x0942,0x2551,0x80C8,0x8058,0xC310,0x6521,0x0000,0x0100,0x2000,0x0100,0x0000,0x0000,0xE418,0x4100,0xA060,0xA438,0xA841,0x6731,0xE839,0xC831,0x1219,0x1511,0xAF00,0xD310,0x1922,0x7619,0x1409,0x5819,0x1D2A,0xDE3A,0x9409,0x9563,0xBE84,0x9F6C,0x9D4B,0xD932,0x191A,0xFA21,0x3A2A,0x5832,0x3E43,0xBE53,0xDE4B,0xBD2A,0x5409,0x8A00,0x1219,0x392A,0x7511,0x3B32,0x5F43,0xFA19,0x3509,0xD408,0xD308,0xFD19,0x1B43,0x7932,0x7219,0x9B2A,0xF811,0x3819,0x5C32,0x5B3B,0x9932,0xFD63,0xD94A,0x984B,0x7019,0x2F42,0x2B6B,0x6B6B,0x6B6B,}, +{0xC3DF,0xC5E7,0xC6E7,0xC6EF,0xC7EF,0xC7F7,0xC7EF,0xC6EF,0xC8EF,0xC7EF,0xC7EF,0xC8EF,0xC9EF,0xCAEF,0xC9EF,0xC7DF,0xC6C7,0x66A7,0xE88E,0xCB5D,0x6C34,0x2B2B,0x6F43,0x3A6C,0xDD84,0x3E8D,0x1F8D,0x1F85,0x7E74,0xDB63,0x7963,0x184B,0xB742,0xBA4A,0x1B53,0x3B53,0x3B5B,0xF94A,0xFD9C,0x5F95,0x1E64,0x3D4B,0xBB42,0x9421,0x9319,0x0E09,0xAF10,0xF210,0x9B21,0x7E5B,0x9832,0x9429,0x6D21,0xC631,0xAF6B,0x6A4A,0x0942,0xA0C0,0xA090,0x6210,0x8629,0x2000,0x0100,0x0000,0x0000,0x0000,0x2100,0xE418,0x4008,0x8060,0x4659,0xEA41,0x4629,0x8629,0xC929,0xAD21,0xD500,0xD408,0xB108,0x5811,0xF510,0xCE08,0xAF08,0xB821,0x9619,0xEE08,0xAA08,0x7010,0x5719,0xD621,0xF721,0x3A2A,0xD519,0xAC00,0x4D08,0xD408,0xF310,0x7919,0x9E2A,0x9C22,0xF819,0x4E09,0x0A09,0x8808,0x8B08,0x3619,0x9411,0x2E11,0x2800,0x8B00,0xB008,0xCE10,0x0C11,0xEC18,0x7C2A,0x7A1A,0x9519,0x1C2B,0x1822,0xBD3A,0x1532,0xDE5B,0xFD53,0x582A,0x1653,0x6B6B,0x6B6B,0x6B6B,}, +{0xC4E7,0xC6EF,0xC6EF,0xC6EF,0xC7EF,0xC7EF,0xC7F7,0xC7EF,0xE8EF,0xC7EF,0xC8EF,0xC7EF,0xC7EF,0xCAEF,0xC8E7,0xC7DF,0x46BF,0x4796,0x2B6D,0xAD43,0xAC2A,0x4A22,0xAE3A,0x174B,0xFD73,0xBF8C,0xFF8C,0x1F95,0x9F84,0x3D7C,0x785B,0xF54A,0x122A,0xD221,0xF431,0xF331,0x1432,0x163A,0xB642,0x1D9D,0x7F7C,0x5F6C,0x3D4B,0xFB4A,0xD121,0xB319,0xCA08,0x3221,0x7719,0xDA21,0xD829,0x7519,0x7121,0x0A3A,0x1274,0x073A,0x6C4A,0xC188,0x80B0,0x2138,0x0421,0x2319,0x0000,0x0000,0x0100,0x0000,0xC210,0x8210,0x2008,0x2068,0x6250,0x6529,0x2429,0x6A42,0x6929,0xAB10,0x4E00,0xAE00,0x2D00,0xB208,0xAC08,0xA700,0x8908,0x6C08,0xEE08,0x0F11,0x3509,0xF208,0x8E00,0x1011,0x3C22,0xFB19,0xB911,0x7111,0x2C09,0xB521,0x7409,0x7719,0x5E2A,0xBF32,0x1C1A,0x3211,0xD811,0x5409,0x8900,0x2608,0xAB08,0xA700,0x8608,0xC808,0x6708,0xE508,0x6210,0xE720,0x7611,0xB601,0x9911,0xF819,0xF711,0x182A,0x7029,0xD521,0x5B32,0xFA42,0x1553,0x6B6B,0x6A6B,0x4B6B,}, +{0xC4E7,0xC7EF,0xC7EF,0xE8EF,0xC8EF,0xE8EF,0xE6EF,0xC8EF,0xC9EF,0xC8EF,0xC7EF,0xC7EF,0xC6EF,0xC7E7,0xC5D7,0x63C7,0x65AE,0x478D,0xC953,0xCC32,0x8C22,0x8B2A,0xED42,0xF142,0xD442,0xB53A,0xD742,0xF94A,0x184B,0x5753,0xF54A,0xD34A,0x5242,0x322A,0x323A,0xEF29,0x1032,0xB029,0xB129,0x5542,0xF829,0xDD4A,0x9F5B,0x3E4B,0x5D53,0x7B74,0x188D,0x6C11,0x5419,0x5419,0x3319,0xF018,0x0D19,0x8A21,0x8F63,0xCF73,0x0842,0x4A62,0x80B8,0x4098,0x6128,0x2521,0x6521,0xE310,0xE310,0xE208,0xA310,0x4108,0x2020,0x2070,0x0441,0x6629,0x093A,0x293A,0x083A,0xE518,0xE718,0x4A11,0x4400,0xAB08,0x6608,0x6808,0x7100,0xCF00,0x1009,0x1609,0x1709,0xD300,0xD208,0x0E09,0x1509,0x1809,0xF708,0x7311,0xF008,0x5819,0x7809,0x3501,0x3511,0x5711,0xD100,0x2708,0x7419,0xD611,0xC908,0x8118,0xC318,0x8210,0x4210,0xA618,0x8308,0x6110,0x8210,0x8510,0x8B11,0x8A00,0x9219,0xF508,0x1612,0x5019,0x2819,0x3011,0xDB42,0xB33A,0xEF5A,0x4B6B,0x6A6B,0x6B6B,}, +{0xC5E7,0xC5EF,0xC6EF,0xC6EF,0xC7EF,0xC7EF,0xC6EF,0xC7EF,0xC8EF,0xC8EF,0xC6EF,0xC5EF,0xC4EF,0xC4EF,0xC5E7,0x44CF,0x05A6,0xA76C,0x8843,0x6C43,0x6E4B,0x9253,0x935B,0x7453,0x3553,0xF44A,0xF44A,0xF442,0x923A,0xB442,0x7663,0x3B74,0xD863,0xF442,0x323A,0x723A,0xF031,0xAF21,0x4F29,0x8D21,0x5121,0x7419,0x9721,0xFA31,0x3C32,0x3E4B,0x7E7C,0xBD7C,0xF221,0x7421,0x7011,0x2F11,0x2B11,0x0611,0x8729,0x137C,0xEC52,0x7263,0x6B72,0x40B8,0x6088,0x6030,0x8218,0xE418,0xC410,0x6210,0x4008,0x2018,0x4058,0x2060,0x2529,0x6731,0xCD4A,0x6A42,0xC731,0x4521,0x2B3A,0x6721,0x6400,0x2500,0x2600,0x4A00,0x4E00,0x4C00,0x2A00,0x1211,0x9200,0x2B00,0x0900,0x4F19,0x3511,0x5711,0xD008,0x3809,0x3709,0x1509,0xB000,0xD000,0x8900,0x2500,0x0300,0x6010,0x8210,0x8618,0xE618,0xE318,0x8531,0x2321,0x2329,0xC739,0xC731,0x0421,0x0329,0xA610,0xAA19,0x2F09,0x781A,0xD008,0xEF08,0x8608,0xE810,0xED18,0x6F19,0x6D4A,0x4B63,0x4B6B,0x6A6B,0x4B6B,}, +{0xC5E7,0xC5E7,0xC6EF,0xC7EF,0xC7E7,0xC6EF,0xC5EF,0xE6EF,0xC7F7,0xC7EF,0xC6EF,0xC5E7,0xC4EF,0xC4EF,0xA4DF,0x63AE,0xC474,0xE84B,0x294C,0x2B44,0xEF43,0xB143,0x5243,0x544B,0x3443,0xB563,0x3A74,0xFA63,0xBB5B,0x585B,0xD542,0xFA52,0x3E7C,0xDF8C,0x3C6C,0x174B,0xB442,0x123A,0xB131,0x8E21,0x7021,0x5019,0x3019,0xF118,0x1411,0x7821,0x192A,0x9A42,0x7A3A,0xFA4A,0xFE5B,0x3B43,0x562A,0x2A11,0xA310,0x0A42,0x337C,0x2B42,0x315B,0xED72,0x2098,0x20A0,0xC090,0xE070,0x8048,0x6038,0x4038,0x4060,0x2068,0x4539,0xC931,0x0A3A,0x0942,0x083A,0x083A,0x0421,0xC418,0x8731,0xC410,0x6208,0x4500,0x2700,0x4900,0x6700,0x4700,0xAD31,0xAD21,0xEF00,0x8900,0xAB00,0xAD00,0x8C00,0x4900,0x6F08,0xD208,0xF108,0x3109,0x7109,0x4B01,0x6300,0x0E22,0x942A,0x2819,0xA531,0xA639,0xEC5A,0x4F63,0x6F63,0xB06B,0xD073,0xEC5A,0x6529,0x4942,0x2621,0x4621,0xEC10,0x0D09,0x4508,0xA910,0x2419,0x8308,0xAA08,0xB321,0xCE5A,0x6B6B,0x6B6B,0x6B6B,0x6B6B,}, +{0xC4E7,0xE4E7,0xC7EF,0xC8E7,0xC7E7,0xC6E7,0xC5EF,0xC7EF,0xC7EF,0xC6EF,0xC5E7,0xE4E7,0xC3E7,0xA3D7,0xE2B6,0xA37D,0xA65C,0x695C,0xAA5C,0x6D4C,0x325C,0x3664,0x7B6C,0x7C74,0x5C6C,0x3B7C,0x3D74,0xDF8C,0xDE8C,0x5D74,0xFB63,0x394B,0x9742,0xFB4A,0x5E74,0xBE84,0x1C64,0xF842,0x1332,0x7119,0x7129,0x9029,0x9021,0x7021,0x3119,0x0F11,0x1119,0x3519,0x1311,0x1311,0x1C3A,0x1F6C,0xBE5B,0xD621,0x4D09,0x4821,0xD17B,0x894A,0xEA41,0x5263,0x7063,0x496A,0x6088,0x2098,0x0090,0x2080,0x2068,0x2551,0x2B42,0x8D4A,0x2A3A,0xAD4A,0xC731,0xC739,0x4429,0xC220,0xE318,0x0419,0xC310,0xA318,0xE418,0x8208,0x2308,0x6400,0x4500,0x2400,0x2200,0x2800,0x6900,0x4700,0x8B00,0x8A00,0x8708,0x4A19,0x4D19,0x5011,0x3309,0xF000,0x6900,0xEA18,0xD329,0x8D31,0xC639,0xAB4A,0x4E63,0x6C52,0x0942,0xC949,0xE949,0xE949,0xED62,0x8F6B,0xEC52,0x6429,0x8529,0xC310,0xE420,0x8631,0x8B42,0xE731,0xA410,0xE810,0x8B21,0xAA52,0x4B6B,0x6B63,0x6B6B,0x4B6B,}, +{0xC4E7,0xC4DF,0xC8E7,0xC8E7,0xC7E7,0xC6E7,0xC5EF,0xC7EF,0xC8EF,0xC7E7,0xC6E7,0xC4DF,0x83C7,0xE2AE,0x4296,0xC485,0x0675,0xE964,0xC95C,0xAB44,0x915C,0x9553,0x9C5B,0xFE73,0x5E7C,0x3E7C,0xBD6B,0xFD73,0x3F7C,0xDE63,0x9C5B,0xF94A,0xB73A,0x5532,0x7742,0xBD63,0xBF84,0x9D7C,0x5B53,0x363A,0xB221,0x2F11,0xEB10,0xCC10,0x1411,0x3519,0x5219,0x0F09,0xAC10,0xA908,0xF210,0x9A21,0x7F53,0xDF5B,0xFD42,0xD419,0x2E42,0xF17B,0x6A42,0x8831,0xEC41,0xB36B,0xB26B,0x4F63,0x8B4A,0x4A4A,0xE939,0x6D4A,0x4629,0x4B42,0x2A42,0x4631,0x2521,0x8731,0x2529,0x2429,0x4529,0x2521,0xE418,0xC310,0xA418,0x0419,0x4108,0x4208,0x4208,0x0100,0x2200,0x2300,0x2400,0x2500,0x0100,0x0100,0x0308,0x4821,0x8808,0x2900,0x0500,0x0B09,0x0911,0x4C21,0x4942,0x694A,0x494A,0xAC52,0x084A,0x8439,0xC128,0xA030,0xC228,0xE328,0xC841,0xAC5A,0x9073,0x6F6B,0x737C,0x4E5B,0x9384,0x4942,0x6729,0x8531,0x0421,0x0519,0x484A,0xEA5A,0x4A6B,0x4B63,0x6A6B,0x4A6B,}, +{0xC3DF,0xC3DF,0xC6DF,0xC7E7,0xE6E7,0xC7E7,0xC6E7,0xC7EF,0xC7EF,0xC7E7,0xC5DF,0xC4D7,0xA3CF,0x63C7,0x03BF,0x86A6,0xA685,0x496D,0x8B7D,0x0D6D,0xD14B,0xF642,0x973A,0x3432,0x1532,0x953A,0xF852,0x1853,0x1953,0xB952,0x953A,0x763A,0x343A,0x132A,0x533A,0x7942,0x9A3A,0x3D53,0x7E5B,0xFC4A,0x783A,0xF419,0x5119,0x0E11,0x2F19,0x7421,0xD521,0xB521,0x1532,0x8F19,0x0B09,0xD110,0x5B19,0x9E42,0x9F5B,0x7D32,0x7519,0x0F3A,0xB16B,0xAF6B,0xA629,0xC939,0xAA39,0x2D4A,0x8E4A,0x8839,0x6531,0xE839,0x8C4A,0x6B4A,0x2529,0x2529,0x2529,0x6529,0xA731,0xC831,0x6631,0xC410,0x8210,0xC318,0xE418,0x6208,0x6208,0x4008,0x4108,0x6208,0x2100,0x2100,0x2200,0x4400,0x6500,0x2200,0x2300,0x2700,0xC900,0x6800,0xC708,0xA518,0xC739,0x6A42,0x4E6B,0x8A5A,0xEC5A,0x2E63,0x6D6B,0x8241,0x6030,0x4038,0x2038,0x2030,0x8649,0x8E6B,0xAD6B,0xCC5A,0x2F6B,0x1374,0x7484,0xB06B,0x8E63,0xCA52,0x8529,0xE839,0xA952,0x0A6B,0x6B6B,0x6A6B,0x6A63,0x4B63,}, +{0xA2CF,0x83CF,0xC6DF,0xC6DF,0xC7E7,0xC6DF,0xA3DF,0xC5E7,0xE5E7,0xC4E7,0xC5E7,0xC5E7,0xC5E7,0xC4D7,0x85C7,0x67BF,0x48B7,0xEBAE,0x0C8E,0x4F5C,0x1143,0x5132,0x1332,0x1232,0xF229,0xF129,0x1032,0x3232,0x1332,0x143A,0x5442,0x743A,0x3232,0xF129,0xEF21,0xF229,0xD121,0xF329,0x1532,0x3732,0x1732,0xD429,0x5019,0xF54A,0xF86B,0xD95B,0x1A43,0x582A,0x1832,0xB629,0x9019,0xCB10,0x7010,0x3811,0x1E2A,0x5F53,0xBD5B,0x1B43,0x572A,0x5142,0xED5A,0x2942,0xC739,0x083A,0x093A,0x2A42,0xAC4A,0xCC52,0x2A42,0x2A42,0x093A,0x8731,0x6729,0xA831,0xC939,0x2529,0x0529,0xE518,0xC318,0xC218,0x8210,0x6108,0x4000,0x4000,0x4100,0x4208,0x4008,0x4208,0x4400,0x6700,0x2400,0x6800,0x6900,0xEE00,0xCC08,0xEB29,0xC510,0xE420,0x0E5B,0x8F63,0x6E63,0x2C63,0xD073,0x2D6B,0xCA62,0x4652,0x2030,0x2038,0x4040,0x2038,0x4459,0x9394,0xD59C,0x748C,0x6F6B,0x7484,0xB28C,0x6F6B,0xED52,0xAB4A,0x073A,0x474A,0xC952,0x2A63,0x4B6B,0x6A6B,0x6A6B,0x4A63,}, +{0x02B7,0x43B7,0xA5D7,0xC4DF,0xA3D7,0xA3CF,0xA4DF,0xC6E7,0xC6EF,0xC6EF,0xC5EF,0xC4EF,0xC4EF,0xC6E7,0xC9E7,0xC9DF,0xC8DF,0xABD7,0x4DAE,0xF08C,0x327C,0x5453,0xD34A,0xD54A,0x5432,0x753A,0x713A,0x0F2A,0xF231,0xD039,0xF139,0x313A,0x944A,0x7432,0x3332,0xF129,0xF131,0xB229,0xD129,0xF329,0x9229,0x9121,0x9221,0xF631,0x7D63,0x7F84,0xFF8C,0x1F85,0x9E7C,0x7B53,0x122A,0x2C19,0xC908,0x8B00,0x7308,0x1819,0xFC31,0x1F5B,0x7F5B,0xD921,0x4D09,0x2919,0x8E31,0x4C29,0xAB31,0x0A21,0x0621,0xE420,0x0621,0x8B21,0xC518,0x0621,0xCC29,0xC618,0xE420,0xC318,0xE518,0xE618,0xC818,0xC418,0x6308,0x6100,0x6208,0x6108,0x8208,0x4108,0x6410,0x6600,0x6908,0x8400,0xAB08,0xF208,0xAD08,0x5209,0x8F19,0xC710,0x8118,0x2521,0x0A4A,0xCB52,0x8A52,0x0C63,0x0C6B,0x6E73,0x8E73,0xEC62,0x8441,0x6040,0x6040,0x4040,0xE571,0x14A5,0x528C,0x9073,0x8F73,0xF07B,0x2E8C,0xAC83,0x5184,0x8F6B,0xEA52,0xC95A,0x0A63,0x4B63,0x4B6B,0x6A63,0x4A63,0x4B6B,}, +{0xE2AE,0x22BF,0xA5D7,0xC4DF,0xC4DF,0xC4DF,0xC4DF,0xE7EF,0xC7EF,0xC5EF,0xC5E7,0xC4EF,0xC4E7,0xC7E7,0xC9E7,0xE7EF,0xCBF7,0xB1E7,0xF7DE,0x1ABE,0xBCA5,0x1C8D,0xBB84,0xBC7C,0xDA5B,0x164B,0x374B,0x364B,0xB342,0x0E22,0xB142,0x923A,0xD442,0xD642,0x563A,0x5332,0xD029,0x533A,0x3432,0x142A,0x143A,0xF429,0x4F19,0x0D11,0xF018,0x1119,0xF739,0xBA4A,0x1B5B,0xFA4A,0x7A5B,0xD863,0x564B,0x5009,0xCA00,0x4800,0xAE08,0x7919,0x1C2A,0x192A,0x1B4B,0x9D5B,0x3C4B,0xBA42,0x5B53,0x3532,0x4821,0x8C31,0x9342,0x9021,0xCC08,0xAB08,0xEA10,0x8808,0xA710,0x0D11,0x7211,0x4F11,0xA808,0x6208,0x0421,0xA410,0x8608,0xA808,0x8A08,0x6208,0x8418,0xA410,0xA808,0x4E19,0x4F11,0x1209,0x3301,0xAD08,0x4408,0xA210,0xC220,0x6629,0xE639,0xC641,0x494A,0x4952,0xEC6A,0x8F73,0x6E73,0xED62,0xAF7B,0x0E84,0x6A6B,0xED83,0x7094,0x108C,0x318C,0xD27B,0xB594,0xD394,0xED7B,0xCF83,0x7ABE,0xF394,0x6D63,0xC95A,0x4A63,0x4B63,0x4B63,0x4B63,0x4A63,0x4A6B,}, +{0xE2AE,0xE1B6,0x44C7,0x64C7,0x84D7,0xC5DF,0xC6E7,0xC8E7,0xC7E7,0xC6EF,0xC5E7,0xC4E7,0xC4E7,0xC9E7,0xC9EF,0xC8F7,0xCCF7,0x74EF,0x5CC6,0xBFA5,0xBFA5,0xBFA5,0x9FA5,0xBFAD,0xDFA5,0xBFA5,0x5E95,0x7C74,0x3753,0x765B,0x3B74,0xBD84,0xFE84,0xBC7C,0xD95B,0x384B,0x1B6C,0x7C7C,0x9E74,0xFD63,0x7B4B,0xF942,0x5832,0x7121,0x8E21,0x766B,0xB763,0x586C,0x786C,0x1864,0xDA53,0x3C6C,0xB942,0x5519,0x5411,0xCF10,0xA808,0x7119,0xBC42,0x1E43,0xDA21,0x5B19,0x7C21,0x3919,0x5821,0x5319,0x4E19,0x9211,0x3111,0xA908,0x0E19,0x9621,0x0E11,0x7321,0x5211,0xEE10,0x5219,0x5311,0x0E11,0xC510,0xEA18,0x2E11,0xEE08,0xCE08,0xAD08,0xC708,0x8208,0x8510,0x7319,0xD519,0xCC08,0x5611,0x9209,0x8608,0x8310,0x8210,0xC318,0x4529,0xA531,0xA739,0x294A,0xE751,0x2752,0xAB62,0x2D6B,0x4E6B,0xF183,0x5394,0x318C,0x1084,0x2C73,0xAA6A,0xEC72,0xAF7B,0x528C,0x8E7B,0xAF83,0x319C,0x75A4,0xF59C,0x8D73,0x0A63,0x4A63,0x4A6B,0x4B6B,0x6A6B,0x6A63,0x4B6B,}, +{0xE3AE,0xC1AE,0xE4B6,0x66CF,0xC6CF,0xC5D7,0xC7E7,0xE8E7,0xE8E7,0xC7E7,0xC6E7,0xC5E7,0xA4D7,0xA9D7,0xC7DF,0xC9EF,0xEAF7,0x0ED7,0x118D,0x755B,0x1743,0x595B,0x7A5B,0xBC6B,0x1D6C,0x7E7C,0xFE8C,0x9F84,0x3E6C,0xBB63,0xF752,0x1A53,0xBD63,0x3E74,0x5F7C,0xDE63,0x5C5B,0x9D63,0x1F74,0x9F7C,0xDF84,0x3F74,0x3D4B,0x5832,0xF421,0x3011,0x5219,0xBA52,0x1E74,0xBF84,0xFF84,0x7F6C,0xDD42,0x3311,0xD008,0xAE08,0xC908,0xA808,0xD310,0x5B19,0x7B19,0x5A11,0x3511,0xB519,0x9521,0x5832,0x7E53,0x3D4B,0x7411,0xE908,0xEA18,0x1311,0x3111,0x1211,0x9421,0xB621,0x9619,0x5B32,0x3519,0xEC10,0xF629,0xD821,0xB721,0x3311,0xEF10,0x0A09,0xE710,0x0C19,0x1622,0x9519,0xAA08,0x6E08,0xCD08,0xEE08,0x8608,0x0911,0xC320,0x4431,0x2431,0x0231,0x2441,0x064A,0xE549,0x895A,0x0D6B,0x4E73,0xF08B,0x6D73,0xAA62,0xE659,0xC651,0x2952,0x8B5A,0xCB72,0xAC9A,0xF1A3,0x54AC,0xB9ED,0x9DEE,0x75AD,0x0B6B,0x4B6B,0x4A63,0x4A63,0x4A63,0x4A6B,0x6A63,0x4A63,}, +{0xC4D7,0xC4DF,0xA5D7,0xC8DF,0xC8DF,0xE8DF,0xC9E7,0xC8E7,0xC8EF,0xC7EF,0xC6E7,0xC6CF,0xA5BF,0xAACF,0xECE7,0xCAE7,0xC9D7,0xCBAE,0x0D6D,0xCE53,0x314B,0x124B,0x5753,0xD64A,0xF74A,0x3853,0x595B,0x7B5B,0x7B53,0x384B,0x174B,0x533A,0xB542,0xB742,0xB94A,0x993A,0x9942,0xB942,0x9A42,0xBA3A,0x3C4B,0x3F6C,0x3F64,0xDD5B,0xBD53,0x3B3B,0x1522,0x5211,0x3311,0x9819,0x3C32,0xDD3A,0x7D32,0xB619,0x7019,0x4E19,0x0B11,0x0B11,0xAF21,0x7621,0x9621,0xF631,0x9D53,0xFF6B,0x5D4B,0x9611,0xF810,0x9B29,0xB721,0xB029,0xFA52,0xBF5B,0x7A32,0x7932,0x9C3A,0x3419,0xF729,0x582A,0x182A,0x773A,0x9621,0x7519,0xF310,0x7419,0xF821,0xD419,0x0E11,0x7021,0x4F11,0x0B11,0xCF08,0x7311,0x1011,0x7409,0x8908,0xE818,0x4529,0xA639,0xA639,0x6631,0x4539,0x6439,0x8541,0x4549,0x8649,0xA649,0x4862,0x0C73,0x2D7B,0x8A62,0x4A62,0xC751,0xA769,0x2CB3,0x57F5,0x3AF6,0x35ED,0x11F4,0xB5FC,0x9DF6,0xEE83,0x4A63,0x4A63,0x4A6B,0x4A6B,0x4A6B,0x4A63,0x4A63,}, +{0xC5E7,0xC6E7,0xC8EF,0xC8E7,0xC9E7,0xC9E7,0xC8E7,0xC8E7,0xC7E7,0xC5EF,0xC5E7,0xA6D7,0xA8CF,0xCEE7,0xCDD7,0xCBC7,0xA9BF,0xAA8E,0x6B75,0x4D5C,0xAF53,0x714B,0x334B,0x5453,0xD76B,0x3A74,0xDB84,0x1C85,0xDC7C,0xBB7C,0x1B64,0x374B,0x785B,0x7C74,0xFD84,0xDC84,0x5C6C,0x7A4B,0xB93A,0x3632,0x7742,0x1C53,0x9E5B,0xFF6B,0x1F74,0x3F6C,0x5E53,0x1822,0x9319,0x9419,0xB419,0xF729,0xF731,0x582A,0x1522,0xF421,0xF121,0x3532,0x5932,0x182A,0x9319,0x0F11,0xF418,0x9B21,0x9B21,0x3511,0xB421,0xBC3A,0xBB32,0x9021,0x3121,0x3519,0x5419,0x5519,0x7721,0xFB42,0x9E5B,0xBC42,0x7E53,0x1A32,0xF829,0x5D43,0x9C2A,0x3932,0xFD42,0x9519,0x3011,0x0F3A,0x372A,0xB219,0x3211,0x5211,0x5311,0x8D08,0x2A11,0xA310,0x0529,0x0539,0xE438,0xC340,0x8138,0x6038,0x8040,0xA148,0xA140,0xA248,0x4451,0x8561,0x8559,0xC260,0x2471,0x8A9A,0xF0D3,0x35FD,0x5BFE,0x7BFE,0xD9FD,0x35FD,0x52FC,0x38FD,0xF3AC,0x4A63,0x4A63,0x4A6B,0x2A63,0x4A63,0x4A63,0x2A63,}, +{0xE6E7,0xC6E7,0xCAEF,0xCAE7,0xCAE7,0xCAE7,0xC9E7,0xC7DF,0xC5CF,0xC5D7,0xC5D7,0xC7DF,0xC8DF,0xCBDF,0xCACF,0x88BF,0xE896,0xE86D,0x2A65,0xAF64,0xF574,0xDA7C,0x9C74,0xFA63,0x1A6C,0x3D74,0x7E7C,0x7E84,0x7E84,0x7E7C,0xFE6B,0x1853,0x753A,0x3832,0x1C53,0x1E74,0x7F7C,0x7F7C,0xFF6B,0x3C4B,0x3732,0xF731,0x9721,0x5819,0x9921,0xDA21,0xBA29,0xD721,0x9521,0x9421,0xF629,0x3932,0x993A,0xDB42,0xB93A,0x993A,0xB83A,0xFA4A,0xDB42,0x5A32,0x9419,0x4D11,0x2F19,0xD008,0x7409,0xD94A,0x1F74,0x7F7C,0x9E53,0xF629,0x4B19,0x4F21,0x9319,0x7B53,0x5F6C,0x7A3A,0x5719,0xD729,0x7819,0x3611,0xBA3A,0x9D3A,0x5619,0x7521,0xF508,0xD008,0x1311,0x583A,0x5F4B,0x5611,0xAC08,0x5311,0xEE08,0x8608,0x8608,0x8128,0xE348,0x8160,0xE370,0xE388,0xE388,0xE380,0x2389,0x43A9,0x27B2,0xA9BA,0x0CBB,0x8DBB,0x51CC,0xB3E4,0xF4EC,0x14F5,0xD4FC,0xB8FD,0x57FD,0xFAFD,0x7CFE,0xD4FC,0x30FC,0x32FC,0xD4CC,0x4A63,0x4A63,0x4963,0x2A6B,0x4963,0x4A63,0x2A63,}, +{0xC5E7,0xC8E7,0xCAEF,0xEAEF,0xCBEF,0xC9E7,0xC9E7,0xCAE7,0xC9E7,0xC8E7,0xC9E7,0xEBDF,0xCADF,0xADCF,0x4BA7,0x0A7F,0x0A7F,0x2B7F,0xCF86,0x1B86,0x3FA6,0x5FB6,0x3FAE,0xFFA5,0x1E85,0x1C64,0x7C53,0xF94A,0x793A,0x5732,0x5632,0x3432,0xD229,0x9121,0x5119,0x7519,0xD821,0xF921,0xF921,0xB821,0x5511,0x3111,0x5111,0x5319,0x1319,0x1211,0x3219,0x9421,0xB321,0xD429,0xF721,0x793A,0xDA42,0xFA42,0xDD63,0x3E6C,0x7E74,0xDF7C,0x9F74,0x9E53,0x392A,0x5732,0x5832,0xB429,0x7832,0x7B3A,0xBA21,0x5821,0x1511,0xEE08,0x3432,0x1C43,0x3C4B,0xDB4A,0xFA29,0x7511,0x7A32,0xDD5B,0x3A2A,0xBB3A,0x3822,0xB008,0xCF10,0x0E09,0xAA10,0x1311,0x7B2A,0x7C3A,0x5B2A,0xB008,0x8C00,0xED08,0xAC00,0x2608,0x4410,0x0048,0xC480,0x08A2,0x09B2,0x45B1,0x44B1,0x25B9,0x85B9,0xC5C1,0xE8D1,0x2DE3,0x4DEB,0x4EF3,0xD1FB,0x91F3,0xB0FB,0xAFFB,0xD0FB,0x32FC,0x57FD,0x3AFE,0x38FD,0x32FC,0x6FFB,0x70FB,0xF1DB,0x2A73,0x2A63,0x2A63,0x4A63,0x2A63,0x4A63,0x2A63,}, +{0xC6DF,0xC8E7,0xCAE7,0xEBEF,0xCBE7,0xC9E7,0xC9EF,0xC9E7,0xC8EF,0xC9E7,0xC9E7,0xA9C7,0xE79E,0xAB96,0x4976,0xE95D,0x6B7E,0xAA86,0x2A76,0x105D,0x164C,0x1B54,0x5C64,0x7D74,0x5E74,0x5E74,0xDE63,0x5B4B,0x9742,0x743A,0x533A,0x112A,0xD029,0x6E19,0x4E19,0x9021,0x7121,0x4F19,0x7219,0x5219,0x7319,0x1011,0xCD08,0xCD08,0xCE08,0x5019,0x9221,0xB421,0xD321,0x1B4B,0xFE6B,0xDE7C,0x9F7C,0x3E6C,0xBF63,0xFE4A,0x5E3A,0xBE42,0xFD4A,0xDD42,0xDC3A,0x7C4B,0xDE5B,0x5F6C,0xBE5B,0xDF63,0x7E32,0x3511,0x5411,0x5219,0xBC5B,0x1F74,0xDD4A,0x9421,0xB319,0xD621,0x9E5B,0x3A32,0x1C2A,0x7D42,0x1409,0x6D08,0x0F09,0x9111,0x8F21,0xB521,0x1E43,0x9521,0xAF10,0xAC08,0x6A00,0x8E00,0x6C00,0x2510,0x0330,0x0060,0x0088,0x62A0,0xA3A8,0xA3B0,0x04B9,0xA3C0,0x46C9,0x46C9,0xA7D1,0xEDEA,0xB0FB,0x73F4,0x51F4,0x51F4,0x11FC,0x73FC,0x94FC,0x94FC,0x95FC,0x53FC,0x4FFB,0x95FC,0x12FC,0xEEFA,0xD1F3,0x0B93,0x4A63,0x2A63,0x4963,0x4A63,0x2A63,0x2A63,}, +{0xC6D7,0xC8D7,0xEADF,0xCCE7,0xEBE7,0xCAEF,0xE9EF,0xC8EF,0xC7EF,0xCAEF,0xCBE7,0xCAD7,0x89B7,0xA88E,0xA765,0x264D,0xC76D,0x6755,0xE74C,0x0C44,0x503B,0x5543,0x584B,0x3843,0x1943,0x3953,0xD84A,0x743A,0x323A,0xF229,0xF221,0xF129,0xD229,0xD229,0x5021,0xCC10,0xAD08,0xEF10,0x1011,0xF010,0xCE08,0xCC08,0xAA08,0xA910,0xED10,0xB329,0x373A,0x973A,0xB93A,0xFC4A,0x3E4B,0x7E5B,0x5E53,0x1E4B,0x3C32,0x9821,0xF108,0x7419,0xD821,0x392A,0x9F5B,0x9F5B,0xDE4A,0x1A32,0x3721,0x5921,0x1611,0x5311,0x792A,0xBB32,0xDB21,0xF508,0x9719,0xB621,0x362A,0x7A4B,0xBC32,0x3211,0x1119,0xD008,0x6B00,0xD55B,0x5532,0xB621,0x3C43,0xF521,0xF729,0xCA08,0xEE08,0x8D08,0x4C00,0xD700,0x9010,0x0420,0x0040,0x0070,0x2190,0x4198,0x41A8,0x63B8,0x63C0,0xA3C0,0xE5C8,0xE4D0,0xE5C8,0xE4D0,0x06E1,0xCAF1,0x8DF2,0x0FFB,0xEFFA,0x91FB,0x70FB,0x4FFB,0x0EFB,0x0EFB,0xD1FB,0xB1FB,0x12FC,0xCDFA,0x2FFB,0x4DC3,0x2A63,0x2A63,0x2A6B,0x2A63,0x2A63,0x2A63,}, +{0xC6D7,0xC6D7,0xC8D7,0xEBDF,0xCBE7,0xCAE7,0xC9EF,0xE8EF,0xE9EF,0xCCEF,0xCCDF,0xCBDF,0xCACF,0x88BF,0x48A7,0xC78E,0x4876,0x485D,0xC533,0x472B,0x2B33,0x904B,0x134B,0xD54A,0x943A,0x333A,0x5232,0xD342,0x1643,0x194B,0x5C53,0x3C53,0xFA42,0x762A,0xB221,0xB429,0xD431,0xB529,0x9219,0x3111,0xEE10,0xEC08,0xAB10,0xA908,0x4E19,0xB842,0xBD5B,0xFD63,0xFE63,0x7E53,0xDE42,0xDA21,0x9619,0x7621,0x5419,0x2F11,0x2E11,0x7319,0x7519,0x7521,0x3719,0xF410,0xD110,0x5019,0xEE10,0x3111,0xD819,0x1A43,0x5F74,0xFF63,0xFC21,0x1209,0xB621,0xD729,0x1E64,0x3E6C,0xD921,0x5111,0x4F19,0x4C19,0xE710,0x966B,0x0D19,0x773A,0x3E64,0x7019,0x9219,0xED10,0x0E09,0x9108,0xD108,0xB418,0x2B20,0x0030,0x0048,0x0070,0x0088,0x00A0,0x21A8,0x22B0,0x83B8,0x84C0,0xA4C8,0xC4C8,0xC4C8,0x05D1,0x67D9,0x26D9,0x26E1,0x46E9,0x06E9,0x68F1,0x0AFA,0x4BFA,0xCEFA,0xF2FB,0xD2FB,0xB1FB,0x91FB,0x6BFA,0x0BFA,0x4AD2,0x2A63,0x2A63,0x2A63,0x4A63,0x2A63,0x4963,}, +{0xC6BF,0xC7D7,0xA9C7,0xCBCF,0xEAE7,0xC9E7,0xC9EF,0xE8EF,0xC9EF,0xCCE7,0xCCE7,0xCAE7,0xC9DF,0xC8CF,0x87BF,0xA696,0xC765,0xC63C,0xA52B,0x4733,0x083B,0xEB32,0xAE3A,0x8F3A,0x512A,0x7242,0xD542,0xD742,0xBC53,0x9D6C,0x1E8D,0x7E8D,0x3E8D,0xBE7C,0x7C53,0x7842,0xB229,0x9121,0x5121,0x3119,0x5119,0x0F11,0xED10,0x4F29,0xFC4A,0x9E63,0xFF63,0x1F74,0xDF63,0xBD42,0xF921,0xD621,0xD629,0xB719,0x7619,0x5219,0x5119,0x9419,0x5619,0xB621,0x1011,0x9321,0x7321,0xF629,0x1643,0xDA42,0x3E64,0x9E53,0xFB29,0xBA21,0x3519,0x1111,0xF829,0xDB32,0x7D53,0xB821,0x5111,0x5219,0xF329,0x3232,0x5232,0xCF29,0xC810,0x9A42,0x9C5B,0x0E11,0x5111,0x1619,0x5411,0x6B08,0xF018,0x0628,0x0130,0x0038,0x0050,0x0068,0x0080,0x0198,0x01A0,0x22A8,0x22B0,0x42B8,0x42C0,0x83C0,0x84C8,0x87C9,0x84D0,0x88E1,0x09E2,0x4AEA,0xE9E9,0x09F2,0xADFA,0x0EFB,0xCCFA,0x6DFA,0x50FB,0xD2FB,0xF3FB,0x0EFB,0x0BFA,0x88E1,0x0A73,0x2A63,0x2A63,0x2A63,0x2A63,0x2A63,}, +{0x44A7,0x06AF,0x888E,0x6BB7,0xCADF,0xC9EF,0xC8EF,0xC8E7,0xCAE7,0xCBDF,0xCBD7,0xCAD7,0xC8D7,0xC7C7,0x46AF,0x6686,0xC65D,0x273D,0x4734,0xE943,0x893B,0x2B3B,0xEF3A,0xD132,0xB43A,0x575B,0x5C74,0x3E8D,0x9F95,0x9F9D,0x7F95,0x3F8D,0x1E6C,0x3C53,0x3A53,0x7A53,0x7953,0xBA5B,0xBB5B,0x1843,0xF529,0x9221,0x7019,0x2F11,0x7429,0xD729,0xD929,0xD721,0xB821,0x9519,0x9429,0x9521,0xF721,0xF821,0xF821,0xD621,0x392A,0x793A,0xB842,0x773A,0xB321,0xD521,0x983A,0x1853,0x7E74,0x1F8D,0x3F74,0x7C3A,0x5411,0x1211,0x2D11,0x132A,0xDE63,0x3F85,0xFC42,0x3219,0x6F11,0xB321,0x593A,0xFD5B,0xF842,0x9021,0x0C11,0xF431,0x3632,0x4C00,0xF408,0x5921,0xD829,0x6410,0xC828,0x8030,0x0038,0x0048,0x0058,0x0070,0x0080,0x0188,0x0090,0x0198,0x00A0,0x00A8,0x01B8,0x01B8,0x22B8,0xA4C0,0x83C8,0x83D0,0xE6E0,0xC5E0,0x27E9,0x46F1,0x68F1,0xCAF9,0x88F9,0xCAF9,0xCEFA,0xAEFA,0xD3FB,0x91FB,0x6CFA,0x67E9,0xC97A,0x2A63,0x2A63,0x0A63,0x2A63,0x2A63,}, +{0xA47E,0x046E,0x298E,0x0BB7,0x6AC7,0xA7C7,0xC8E7,0xCAE7,0xCAE7,0xEAE7,0xC9D7,0xC9CF,0xC9CF,0xC8BF,0x0797,0x286E,0x894D,0xE83C,0xC93C,0x0A3C,0xAC3B,0x6E43,0xF042,0xB23A,0xB53A,0x175B,0x9742,0x583A,0x7742,0xB752,0x553A,0xB74A,0x194B,0x5C74,0x1E8D,0x3F8D,0xBF9D,0x9F95,0xFF8C,0x9E63,0x7932,0xF221,0xAE21,0xAE19,0xAE19,0x4F11,0x7021,0xD521,0xD521,0x3732,0xBA3A,0xBC3A,0xDE4A,0xBE3A,0x9C3A,0xBC32,0xBC3A,0x3B43,0x9C5B,0x9D53,0x7B32,0x3A32,0xD83A,0x9B53,0x5C53,0xFB4A,0xDB29,0xDA21,0xD629,0xD629,0xD021,0xB842,0x5E74,0x3E53,0xF921,0x9419,0x9211,0xF629,0x5D53,0x5F8D,0xB842,0x7019,0x0E11,0x2D11,0x2E11,0x6F00,0x3911,0x1919,0x1419,0x4020,0x6030,0xA040,0x0048,0x0058,0x0060,0x0070,0x0078,0x0188,0x0088,0x0088,0x0090,0x0098,0x22A8,0x00B8,0x01B8,0x02B8,0x02C0,0x26D1,0x67D1,0xA3D8,0xE5E0,0xE5E8,0x06F1,0x47F1,0x26F9,0x88F9,0x68F9,0xAAF9,0x51FB,0x91FB,0xEAF9,0x88F1,0xA992,0x0A63,0x2A5B,0x2A63,0x2963,0x2963,}, +{0xC48E,0xA57E,0xC69E,0x29AF,0x49B7,0xC8D7,0xC9E7,0xCBE7,0xCBD7,0xC9CF,0xA9B7,0xAABF,0x4BB7,0x499F,0x098F,0x286E,0x4845,0x0734,0x2834,0x2C44,0x1154,0xD653,0x7753,0x7A4B,0xF942,0x973A,0x7732,0xF429,0xB221,0x7019,0x6E19,0x9542,0x1A5B,0x1A5B,0xD952,0x9842,0x9842,0x7842,0x3A32,0x5922,0x9A53,0x5B5C,0xBE74,0x5F74,0x3D4B,0xF521,0x7121,0x5011,0x5011,0xB219,0x7519,0x3711,0x1811,0x5611,0xFB4A,0x1D64,0xBF74,0xBF7C,0x3F6C,0xFD4A,0x9A3A,0xBC7C,0x1F85,0x3D43,0x7319,0x7119,0x5319,0x9221,0x3632,0x5832,0xB93A,0xD942,0xBE5B,0xDD3A,0x3411,0x1111,0x3219,0xF421,0x7B5B,0x5C74,0x4F19,0x0B11,0x152A,0x1011,0xB511,0x6E08,0x1811,0xF510,0x2710,0x4028,0x6040,0x0050,0x0050,0x0058,0x0060,0x0068,0x0070,0x0078,0x0088,0x0088,0x0090,0x00A0,0x01A8,0x01B0,0x00B0,0x84B8,0xA4C0,0x22C8,0x21C8,0x22D0,0x63D8,0x43E0,0x63E0,0x83E8,0xA4F0,0xA4F0,0xC6F0,0x28F9,0xEBF9,0x6DFA,0xCAF9,0x68F1,0x27A2,0x2A63,0x2963,0x295B,0x2963,0x295B,}, +{0x648E,0x05A7,0x88BF,0xC9CF,0xCAD7,0xC8D7,0xC8D7,0xCAD7,0xC9BF,0x89B7,0x2BA7,0xAC9E,0x6B86,0xCB7E,0xAA7E,0x6855,0x6734,0xE733,0x0A44,0xB244,0xDA54,0x7E5C,0xBD5B,0xBD5B,0x9D63,0x3C53,0xDB4A,0xB942,0xB742,0x963A,0x3432,0xF321,0x7119,0x4E11,0x2D19,0x132A,0xDA3A,0xDB3A,0xDE5B,0xBF9D,0xDFA5,0xBF9D,0xFF8C,0xDD63,0x3932,0x7532,0xB85B,0x1D64,0xFD5B,0xDC32,0x9421,0x3111,0xD110,0xB329,0x3B3A,0x1D3A,0x5D32,0x3C32,0x5B74,0xDB7C,0x1E85,0x5E74,0xDD4A,0x7719,0x9319,0xB529,0xD629,0x3832,0xBA3A,0x5E6C,0x7F6C,0xDD63,0xBE5B,0xB919,0x3211,0xEE10,0xD421,0xF329,0x783A,0x9A32,0xB221,0xC708,0xD229,0x1119,0x5C2A,0xF108,0x6C08,0xAB10,0x6210,0x4018,0x0030,0x0050,0x0060,0x0060,0x0068,0x0070,0x0070,0x0078,0x0088,0x0088,0x0098,0x0098,0x00A0,0x00A8,0xA3B0,0xC4B8,0x24C0,0x01C0,0x01C0,0x21C8,0x01D0,0x01D8,0x43E0,0x43E8,0x83E8,0xA3F0,0xA5F0,0xE6F8,0x07F9,0xEBF9,0x47F9,0x46F1,0xC7B1,0x2A63,0x2A63,0x0A63,0x2A63,0x2A63,}, +{0x8955,0x4A76,0x8DB7,0xECCF,0xCBCF,0x89C7,0x68AF,0xABBF,0xAAB7,0x8886,0x4B7E,0x8C75,0x0D86,0x0B7E,0x4A55,0x8A5D,0x6834,0xCB44,0xF064,0xB44C,0x5A4C,0x1D5C,0x1E6C,0x9C53,0x5F74,0x5E74,0x7E74,0x9E74,0x5E74,0xDD5B,0xF842,0x532A,0xB23A,0x5532,0x9A32,0x7E5B,0xFF63,0xFD6B,0xBC6B,0x7D63,0x5E5B,0x1B53,0x1953,0x9B63,0x3E74,0x7E84,0x9E8C,0xDC6B,0xDA4A,0xD829,0x1211,0xCA08,0x8A08,0xAC08,0x1111,0x1209,0x1311,0x5619,0xFA9C,0xB963,0xB729,0x1519,0x5611,0x5519,0xB521,0x3832,0x593A,0x793A,0xBF7C,0xFF84,0xFD42,0x3832,0x3619,0x1211,0xB221,0xED31,0x8A29,0xEF29,0x9A5B,0x7E64,0xB63A,0x2E11,0x2F11,0xED18,0x7B42,0x9621,0x6A00,0x4210,0x6010,0x8018,0x2020,0x0040,0x0060,0xA270,0x0070,0x0070,0x6278,0x0078,0x0088,0x0088,0x0090,0x0098,0x00A0,0x00A8,0x63B0,0xC4B0,0x21B8,0x22C0,0x63C8,0xA7C9,0x00D0,0x01D0,0x02E0,0x02E0,0x84E8,0x83E8,0x43F0,0x63F0,0x06F1,0x47F9,0x06F9,0xE5F0,0x66B9,0x0A63,0x0A63,0x2963,0x295B,0x295B,}, +{0x083D,0x6B5D,0x4C86,0x4C9F,0xA986,0xA86D,0x277E,0x8A86,0xEA96,0xAA86,0x2A4D,0x2D55,0xAF7D,0xCD7D,0xAE5D,0x0F6E,0x4F5D,0xAE4C,0x6F4C,0xCF43,0xB443,0x9C74,0x1E5C,0x5F6C,0xFF84,0x3F8D,0x5F8D,0x1F85,0x9E7C,0x5C5B,0xF942,0x1953,0x1753,0xB94B,0x9E74,0x9F74,0x7F5B,0x9719,0x8D08,0x8B00,0x7010,0x9010,0xB010,0xF218,0xB008,0xEE10,0x7321,0x9521,0x3419,0xAE08,0xCB08,0xED08,0x6C11,0x713A,0x1422,0xD419,0x7319,0x3319,0x1111,0xB108,0xD008,0x3311,0xD008,0xF310,0xF829,0x3832,0x1C4B,0x1D6C,0x3F6C,0x5C3A,0x5719,0xF208,0x0F11,0x5019,0x102A,0x8F3A,0x6B42,0x8A31,0x1B74,0xBC5B,0xCD10,0xB421,0x993A,0x7219,0x7721,0x5611,0x1209,0xAA10,0x2010,0x2020,0x2028,0x0028,0x0058,0xC278,0x4070,0x0070,0x0078,0x0080,0x0088,0x0090,0x0098,0x00A0,0x00A0,0x00A8,0xC4A8,0x62B0,0x01B8,0x01B8,0x00C0,0x01C8,0x00D0,0x01D0,0x02D8,0x22E0,0x43E0,0x42E8,0x63E8,0x64F0,0xE7F0,0xA9F9,0xE5F8,0xE8F1,0x04B9,0x296B,0x2963,0x095B,0x2A5B,0x295B,}, +{0xE72C,0xA94C,0x4955,0x0A66,0x296E,0x664D,0x054D,0x875D,0x496E,0x2A66,0x8A55,0x0B4D,0x6E75,0x6E5D,0x4E5D,0x0F55,0x6F4C,0xAB43,0x0933,0x2B43,0xEF3A,0x5A6C,0xDD5B,0x1F64,0x5F7C,0xBD63,0x3B53,0xDB63,0x1C74,0x184B,0x995B,0x9C7C,0x3F95,0xBF8C,0xFE73,0xFB52,0x1A32,0xB619,0xCF10,0x8708,0x8500,0x8400,0xC708,0x2D11,0x5119,0x3221,0x8C08,0x8808,0x0C09,0x5722,0xF942,0x1A4B,0x3A53,0xB552,0xDA4A,0xF829,0x1011,0xAC00,0x4F11,0x9511,0x1001,0xAB00,0xCA08,0x9229,0xF621,0xFB42,0xDF84,0xDE5B,0xD921,0x7619,0x7619,0x181A,0xB421,0x2E19,0xB129,0x703A,0x6E42,0xCA29,0x7342,0x4D21,0x6408,0x9129,0x995B,0x8D08,0x9310,0x1932,0xF410,0xD118,0x4518,0x2028,0x0030,0x2038,0x2040,0x2070,0x8080,0x2078,0x0078,0x0088,0x0088,0x0090,0x00A0,0x62A8,0x00A0,0x01A8,0x63B0,0x01B0,0x00B0,0x00B8,0x00C0,0x01C8,0x00C8,0x00D0,0x02D8,0x22D8,0x02E0,0x84E8,0xA4E8,0x83E8,0x84F0,0xA5F0,0xC5F0,0xC5E8,0xE4C0,0x096B,0x295B,0x0A5B,0x2963,0x0A63,}, +{0xE73C,0x8B65,0xAB65,0xC95D,0x895D,0xA534,0x6334,0x643C,0x285D,0x885D,0x4745,0x872C,0x6944,0x4944,0xAA3C,0x0C34,0x8B3B,0x2B33,0x8922,0xEA32,0x6B2A,0x334B,0xB642,0xF721,0x983A,0x9B63,0xDD6B,0x5963,0xB231,0xB442,0x195B,0x7742,0x5321,0x9319,0x573A,0x5B32,0x9531,0x2D19,0x2911,0x0C11,0x2B19,0x6F21,0x1019,0xCD10,0xCA08,0x2A11,0xCD29,0x5232,0x9A3A,0x193A,0x5321,0x7021,0x5532,0x5953,0x5B53,0xDB3A,0x3943,0x1D64,0xFF63,0x9F53,0xFA21,0xAD00,0x0D11,0xF729,0xBB42,0x9E7C,0x7E5B,0x9921,0x7711,0xB721,0x9D53,0x5D4B,0x9411,0xB221,0x9121,0x753A,0x322A,0x4B21,0x0719,0x0811,0xC408,0xEE18,0xDC5B,0xEE08,0xAD08,0xCF18,0x6A08,0x8C10,0x0018,0x0028,0x0038,0x0040,0x2048,0x2060,0x0080,0x2080,0x2088,0x0088,0x0090,0x0090,0x0098,0x21A0,0x00A8,0x01A8,0x02B0,0x21A8,0x00B0,0x00B8,0x00B8,0x01C0,0x01C8,0x00D0,0x01D8,0x01D8,0x00E0,0x01E0,0x22E8,0x22E8,0x43E8,0x84F0,0xA5F0,0xC4E8,0xE5C8,0x096B,0x2A5B,0x0A63,0x0963,0x0963,}, +{0x0645,0x8A65,0x084D,0x694D,0xA734,0x8634,0x442C,0x0324,0xC333,0x453C,0x653C,0x6734,0x093C,0xC943,0x262B,0x0934,0xAB2B,0x4B2B,0xAB2A,0xAB32,0x4B32,0xCA19,0x6F42,0x3532,0xB729,0x1611,0xB100,0x0F09,0x9319,0x3121,0x2C19,0x7129,0xB529,0xD731,0x1019,0x6C21,0x5132,0x7432,0xF329,0xB021,0x8F19,0x8F21,0x8F19,0x2D19,0xE910,0x0A19,0x0B19,0xC910,0x8710,0x4921,0x9442,0xFC63,0x9F74,0x7F74,0x1F6C,0x9F7C,0x3E9D,0x5C7C,0x3953,0x7329,0x8A00,0xCB08,0x3221,0x3B3A,0x7D53,0xBC42,0xDB21,0x5611,0xB719,0x5D53,0xDF63,0xDA21,0x3111,0xD429,0xB621,0x5B4B,0x342A,0x8608,0xA508,0x8608,0x8708,0x8F10,0xFB4A,0xB719,0x3211,0x8B08,0x6600,0x6410,0x2110,0x0020,0x0030,0x0040,0x0050,0x2058,0x0070,0x0080,0x0088,0x0090,0x0090,0x0098,0x0098,0x00A0,0x00A8,0x00A8,0x00A8,0x00B0,0x00B0,0x00B8,0x00B8,0x01C0,0x01C8,0x00C8,0x00D0,0x00D8,0x00D8,0x42E0,0xA4E8,0x42E8,0x21E8,0x63E8,0x84F0,0x82E8,0x84C8,0x096B,0x295B,0x295B,0x295B,0x295B,}, +{0x063D,0x695D,0xAA55,0xAB4D,0x0A45,0x252C,0xA42B,0x221B,0x2223,0x442B,0x652B,0xE82B,0x071B,0xC622,0x893B,0xC93B,0x4B34,0xAC23,0xEA22,0xAB2A,0x081A,0xC819,0x8921,0x8D19,0x2F11,0x7419,0x9411,0xB621,0x3319,0x3011,0x2E19,0xED18,0xA810,0x4B21,0x7532,0x9C53,0xDE53,0xFD42,0x9A3A,0x1432,0xF221,0xF329,0xF329,0xCF19,0x2A19,0xC610,0xA610,0xC510,0xC808,0x6D21,0x3732,0xDB4A,0x1E53,0x9C3A,0x9942,0x3219,0x8E00,0x8900,0x2400,0xCC08,0x0F11,0x1211,0x5219,0x793A,0xBA3A,0x1D43,0xB919,0xD208,0xD529,0xBD63,0x5819,0xF208,0xD108,0x7411,0xDC42,0xDE5B,0x7219,0x4600,0x4708,0xAB00,0x8B08,0xCE08,0x1111,0x9721,0x1922,0xEE08,0xA900,0x6508,0x4310,0x0120,0x0030,0x0040,0x0050,0x2068,0x2068,0x0078,0x0090,0x0098,0x0098,0x0098,0x00A0,0x00A8,0x00A0,0x02B0,0x00A8,0x00B0,0x00B8,0x00B8,0x01C0,0xA4C8,0x00C0,0x00C8,0x00D0,0x21D8,0x62D8,0x21E0,0x42E0,0x22E8,0x00E8,0x22E8,0x21E8,0x01E0,0x42C8,0x096B,0x295B,0x2963,0x2A5B,0x0963,}, +{0xA644,0x4B6D,0xEA4C,0xEA3C,0x073C,0xC31A,0x042C,0x0534,0x443B,0xA112,0x652B,0x682B,0xA82B,0x4923,0x2933,0x493B,0x0B34,0xEC2B,0x8C23,0x6C23,0x8B22,0xA711,0x4509,0x4819,0x8D19,0x7119,0x3211,0x1011,0x3211,0xF018,0xAB08,0x4A19,0xB532,0xFD63,0xBF84,0x7F7C,0x1C5B,0xD729,0x7121,0x9219,0x1632,0x3532,0xF329,0x8F21,0x4B21,0x2711,0x0619,0x0911,0x2E19,0x4F19,0xD321,0x9B3A,0xF721,0x9211,0x3532,0xB721,0xCF08,0x4500,0xAC08,0xF008,0x1111,0x5519,0xF729,0x1832,0x3E6C,0x9D3A,0x7711,0xAE08,0xAF08,0xD008,0x8C00,0x8D08,0x4A00,0x3411,0xDD5B,0x7B32,0xAB00,0x6800,0x6C08,0x1511,0xAE08,0xAC08,0xAD08,0xF410,0xBA21,0xCF08,0x8B08,0xAC08,0x8410,0x2220,0x0030,0x0040,0x0058,0x0068,0x2078,0x8078,0x6090,0x0098,0x00A0,0x00A0,0x00A0,0x00A0,0x00A0,0x01A8,0x63B0,0x82B0,0x01B8,0x00B8,0x00B8,0x43C0,0x01C8,0x00C8,0x01D8,0x22D8,0x42D8,0x22D8,0x01D8,0x00E0,0x00E0,0x21E8,0x20E8,0x00E0,0x63C8,0x0A73,0x0A5B,0x0A63,0x0A5B,0x095B,}, +{0x632B,0xA533,0x842B,0x642B,0xE423,0x263D,0x0645,0x031B,0x420B,0xE423,0x872B,0xCA33,0x8B33,0xEA22,0x4712,0x082B,0xCC3B,0xEE3B,0x6E33,0x2C2B,0x8D22,0x8E2A,0x2A1A,0xA711,0x8811,0x0809,0x4E11,0x3111,0x3019,0xED10,0x0F32,0xD94A,0x395B,0xB652,0x1119,0x6B00,0x2600,0x4400,0x2911,0xB342,0x343A,0x5532,0x7732,0xF429,0x6E19,0x2A19,0x6919,0xAE29,0xD121,0x142A,0xD942,0x3B53,0xF94A,0x9A3A,0x1311,0xA900,0x8400,0x3019,0x5211,0xEF10,0xF218,0x7421,0xB629,0xBD5B,0x1E4B,0x7D22,0xF408,0xD108,0xD108,0xAC08,0x6708,0x6500,0x6908,0x1019,0xBB3A,0x5211,0xCC08,0x4600,0x6B08,0x9719,0xD010,0xEF08,0xCE10,0x7419,0x1219,0xCD08,0xAF08,0x1009,0xC810,0x4520,0x0128,0x0040,0x0050,0x0068,0x8280,0x8689,0x8188,0xA1A0,0x0098,0x00A8,0x00A8,0x00A8,0x00A8,0x83B0,0xA3C0,0x01B8,0x00B8,0x00B8,0x00B8,0x00C0,0x00C8,0x00D0,0x01D0,0x01D8,0x01E0,0x00E0,0x00E0,0x00D8,0x83E0,0x42E8,0x21E0,0x00E0,0xE5C0,0x4B73,0x2963,0x2963,0x095B,0x085B,}, +{0x4543,0x662B,0x6734,0x4C4D,0x8B4D,0x062C,0x8112,0x441C,0xC62C,0xC72B,0x0723,0xC922,0xA81A,0x4A1A,0x8922,0xC932,0x4D33,0x0E2B,0xF02A,0xF232,0xF53A,0xD33A,0xB12A,0x2A1A,0xC821,0xAD11,0x9011,0x6F19,0x4C19,0x6D29,0xC810,0x8500,0x8700,0x8D08,0xAD08,0xAD00,0xCB08,0x4C11,0xD63A,0x7A4B,0xFA52,0xD529,0x763A,0x5632,0xD129,0xCE19,0x102A,0xD121,0xF529,0x5B53,0x7C53,0x5932,0x192A,0x5A2A,0xB619,0xEC08,0x5111,0x3119,0x6608,0xAB08,0xED08,0x3119,0x583A,0xFD42,0xBE3A,0xBA19,0xF408,0x1311,0xF000,0x8900,0x8608,0x8700,0xAB08,0x5111,0xCE08,0x9519,0xF619,0x8900,0x6D00,0x7A19,0xF210,0xF010,0xF018,0x3B32,0xB419,0xAE08,0xB000,0x1011,0xCB18,0x6518,0x0130,0x0040,0x0058,0x0068,0x0078,0x4288,0x2190,0x8298,0x6098,0x0098,0x00A8,0x00A8,0x00B0,0x00A8,0x00B0,0x00B0,0x00B0,0x00C0,0x00C0,0x00C0,0x01C8,0x00D0,0x00D8,0x01D8,0x01D8,0x00D8,0x00D8,0x00D8,0x00D8,0x00E0,0x20E0,0x20D8,0xE4B8,0x0A6B,0x0A63,0x0963,0x095B,0x0A5B,}, +{0x8943,0xCD5C,0x2C55,0xCA4C,0x0534,0xA323,0xAB45,0xAB45,0x882C,0x461B,0x6733,0x4923,0xEA22,0x4E3B,0xCE2A,0xCC32,0x0E33,0x9133,0xB543,0x773B,0x1533,0x5643,0x923A,0x4E2A,0x4F2A,0xCF19,0x6F21,0x4D19,0x4C11,0x0819,0x2609,0x0611,0x0C11,0x5211,0x3319,0x9421,0x7121,0xD963,0x7C5B,0x9742,0x5732,0x5932,0xD942,0xDA42,0x5C4B,0x772A,0xF121,0x332A,0x783A,0x3B53,0x3C5B,0x1C43,0xDE5B,0x5E4B,0xB819,0x5319,0x6E19,0xC808,0x8708,0x4F11,0xD729,0x9521,0x3832,0xFD4A,0x1B2A,0x1309,0xD210,0xD110,0xAE00,0xAC08,0xAA08,0xEC08,0xEE10,0x3011,0x1019,0xBD3A,0x5C2A,0x8B00,0x8E08,0x3A19,0x1319,0xD010,0xF210,0xFC39,0xD942,0xAE08,0x6C00,0x8E10,0xAA10,0x6918,0x0230,0x0040,0x0050,0x0068,0x0078,0x0078,0x0090,0x0088,0x0098,0x40A0,0x00A0,0x00A8,0x00B8,0x00B0,0x00B0,0x00B0,0x00B8,0x00B8,0x00C0,0x00C0,0x00C8,0x00C8,0x00D0,0x00D0,0x01D8,0x00D8,0x00D0,0x00D8,0x83E0,0x20E0,0x00E0,0x00D8,0x25B1,0x0963,0x0A63,0x0963,0x2963,0x095B,}, +{0xAC4B,0xAA3B,0x0844,0x2634,0xC323,0xCC3C,0x8C3C,0xE823,0xC613,0x0824,0xA82B,0xA823,0xEE3B,0xCF33,0x2D33,0xEC32,0x6F3B,0x722B,0x3333,0x9543,0x7743,0xB42A,0x5332,0x5132,0x522A,0x332A,0xD221,0xD221,0xB021,0x8E19,0x8A19,0x0C22,0xF121,0xF421,0x9431,0xD229,0xB84A,0xB652,0xD429,0x192A,0x352A,0x7B53,0xBF5B,0xBF63,0x1E4B,0xF729,0xB121,0xF431,0x783A,0xBC42,0xDE5B,0x5F6C,0x7F53,0xDB21,0x9619,0x1422,0x8F21,0xCA08,0xD129,0x182A,0xB621,0x1722,0xB821,0x3C2A,0x7711,0xAF00,0xAF08,0xAF08,0xD108,0xAD08,0xAB10,0x2D19,0x0F11,0x2F11,0x793A,0xBF53,0x5C32,0xAF00,0xAE00,0xAE08,0x8D08,0xB008,0xB208,0x5921,0xBD42,0x6E08,0x4800,0x6C08,0x6C10,0x4810,0x0328,0x0038,0x0048,0x0060,0x0070,0x0078,0x0088,0x0090,0x0098,0x0098,0x40B0,0x20A8,0x00A0,0x00B0,0x00B8,0x00B8,0x00B8,0x00B8,0x83C0,0x20C0,0x00C8,0x00C0,0x00C8,0x00D0,0x00D0,0x00D0,0x00D8,0x00D8,0x00E0,0x00E0,0x40E0,0x20D8,0xC5A1,0x095B,0x0963,0x0A63,0x095B,0x0963,}, +{0xA53A,0x072B,0xC73B,0x4734,0xCF45,0xAF34,0x2723,0xAA33,0xE92B,0x6723,0x461B,0xC82B,0x2B24,0x4D34,0xCE33,0xED32,0xCD3A,0xAE2A,0x9543,0xF542,0x7232,0x732A,0x7532,0xD021,0xCE19,0xB63A,0x5532,0x142A,0x3432,0x132A,0xCF21,0xED21,0x112A,0xCF19,0x184B,0x3B5B,0xD221,0xF93A,0xFC42,0x7A32,0x1D4B,0x5E53,0x7E53,0x5E53,0xF829,0x7019,0x9221,0xD329,0x9A42,0x9E5B,0x1F6C,0x3F53,0xFC29,0x5719,0xD83A,0x182A,0x8B08,0x0D09,0x7B3A,0x7A3A,0xD729,0xD721,0xBC3A,0xBB21,0xF100,0xAE08,0xB008,0xD008,0xAF10,0x0F11,0xEB10,0x0C19,0x5111,0xB521,0x1A3A,0xFD42,0x3C2A,0xF108,0xAE00,0x6F08,0x4B00,0x6E00,0xF308,0xD508,0x5919,0x4A00,0x4800,0x2808,0x7010,0x6D18,0x0228,0x0038,0x0048,0x0060,0x0068,0x0080,0x0080,0x0090,0x0098,0x0098,0x2098,0x80B0,0x40A8,0x40B0,0x00A8,0x00B0,0x00B8,0x00B8,0x22C0,0x00C8,0x00C8,0x00C8,0x00C8,0x00C8,0x00C8,0x00D0,0x00E0,0x00D8,0x00E0,0x20E0,0x20E0,0x20D8,0x489A,0x0A5B,0x0963,0x095B,0x0963,0x0963,}, +{0xC63A,0x4733,0x0B4C,0x905D,0x0D34,0xE71A,0x2823,0x2A23,0xE822,0x861A,0x6412,0xA51A,0xEA2B,0xA923,0xA81A,0xA822,0xED32,0x3043,0x4F2A,0x4F2A,0xB02A,0xB532,0x1122,0xF219,0x322A,0xF221,0x562A,0x5632,0x3632,0x3532,0x5632,0x9532,0x542A,0x9742,0xD221,0x144B,0x7E74,0xBD63,0xDC42,0x392A,0xFB3A,0xFA4A,0x3C53,0x9529,0x1111,0xB319,0x0F19,0x1532,0x1B43,0x9E63,0x7D32,0xFB21,0x7819,0x162A,0xB642,0x6B00,0x5319,0x7419,0x5419,0x1111,0x5732,0x7E53,0x7C32,0xF310,0x8E08,0x6908,0xAC08,0xAE08,0xF110,0x9121,0x5011,0x2F19,0x9119,0xB521,0x172A,0x1A2A,0x3D2A,0x3311,0xB110,0x7008,0x6F00,0x4D00,0xD508,0xB108,0xAE08,0x4A08,0x0908,0x0608,0x0910,0x2E18,0x0528,0x0038,0x0040,0x0058,0x0068,0x0078,0x0088,0x0090,0x0090,0x0098,0x00A0,0x2098,0xC0B0,0xA0B8,0x40B0,0x00A8,0x00A8,0x00A8,0x00B8,0x00C0,0x00C8,0x00C8,0x00C8,0x00C8,0x00D0,0x00D8,0x00E0,0x00E0,0x20E0,0x00E0,0x40E0,0x60D8,0x678A,0x0963,0x0963,0x095B,0x0963,0x095B,}, +{0x264B,0xA843,0x0B54,0x2A44,0x8B3B,0x6A33,0x6A2B,0x691B,0xA71A,0x661A,0xE311,0x0312,0x0512,0x661A,0xC92A,0xCC2A,0xEC32,0x8B22,0x0A22,0x8C32,0x4D2A,0x0F1A,0x742A,0xD119,0x322A,0x1322,0xF319,0x7832,0x773A,0x783A,0x7732,0x973A,0xD321,0x4C19,0xF442,0x9F7C,0xFA52,0xD74A,0xB529,0xB93A,0xF921,0xF831,0x1411,0x3311,0xD821,0xCD08,0x7119,0x963A,0xDE63,0xBD3A,0x3B32,0xF931,0xD210,0x3811,0x8F00,0x1119,0x6C08,0x2400,0xB319,0xDD3A,0x3F4B,0xFC21,0x1511,0xAF08,0x8D08,0x6B00,0x8C00,0x1011,0x5319,0x9521,0x7421,0x9521,0x5A32,0x9519,0x3219,0x9821,0xFE31,0x3511,0xB308,0x9008,0x4E00,0x6F00,0xB708,0xF308,0x6D00,0x4A00,0x0708,0x0510,0x0318,0x0220,0x0128,0x0038,0x0040,0x0050,0x0060,0x0070,0x0078,0x0088,0x0090,0x0098,0x00A0,0x00A8,0x00A0,0xC0B8,0xE0B8,0x60B0,0x00A8,0x00A8,0x00B0,0x00B0,0x00C0,0x00C8,0x00C8,0x00D0,0x00D0,0x00E0,0x21E8,0x21E0,0x20E8,0x20E8,0xA0E8,0x61D0,0xC972,0x095B,0x095B,0x095B,0x0963,0x095B,}, +{0xE54A,0x884B,0x0964,0xC95B,0x4A44,0x2A44,0xA93B,0x681B,0xC612,0xC51A,0x6312,0x4412,0x4312,0x8622,0xC922,0x681A,0x8922,0xAA22,0x4A22,0xE929,0x8C32,0xAE22,0x5122,0x111A,0xF119,0x722A,0x342A,0x3522,0x162A,0x552A,0x553A,0x763A,0xD429,0x343A,0xDB4A,0x182A,0x194B,0x1632,0xD742,0x1732,0xF108,0x1111,0xB321,0x393A,0x8D08,0x4D11,0x5532,0x9B3A,0x3D4B,0x7C3A,0x3221,0x6900,0x1309,0x8D00,0x4600,0x6900,0x4700,0xD219,0xDC42,0xBD42,0xB921,0xCF08,0xAD00,0x8E08,0x8D00,0x4C00,0x8D08,0x1111,0x9529,0xD529,0xB531,0xFC42,0x1C4B,0x3319,0xD008,0x7921,0xBB21,0xD208,0x9008,0x8C08,0x4A08,0x4C00,0x7308,0xF708,0x6E00,0x4900,0x4408,0x4210,0x0020,0x0020,0x0028,0x0038,0x0040,0x0050,0x0060,0x0070,0x0078,0x0080,0x0088,0x0090,0x00A0,0x00A0,0x00A8,0x00A8,0x40A0,0x80B0,0xA0B8,0x60B8,0x20B0,0x00B0,0x00B8,0x00C8,0x20C8,0x00D0,0x00D8,0x00E0,0x00E8,0x00E0,0x20E8,0x40E0,0x80E8,0x24C9,0x0963,0x0963,0x0A5B,0x295B,0x095B,0x095B,}, +{0x0453,0x846B,0x8693,0x4783,0xA963,0xC843,0x473B,0xA522,0x8412,0x6212,0xE41A,0xE51A,0x651A,0x6312,0x4422,0xA822,0xA822,0x8A22,0x091A,0xAF2A,0x8E32,0x5033,0xD22A,0x512A,0xCE19,0x8F11,0x0F22,0xF219,0x9632,0xD229,0x562A,0x3732,0xEB10,0xCA08,0x1843,0xBE5B,0xF831,0xB642,0x1642,0xED08,0x4E11,0xD721,0x9721,0x2800,0x8E21,0x1D4B,0xDC42,0xB942,0x784A,0x8C08,0x6408,0xEF08,0x4A00,0x4200,0x6608,0xB021,0x7842,0x3519,0xD008,0xD008,0x8E08,0xD210,0xAE00,0x6C00,0x6C00,0x6B00,0x4800,0xAD10,0x3319,0x5319,0xF731,0xBF5B,0x5A3A,0x3519,0xB110,0x1711,0x1611,0xD108,0x8D08,0x8A08,0x6808,0x6C08,0x9200,0xF908,0xAD00,0x6700,0x4408,0x4218,0x0020,0x0020,0x0030,0x0038,0x0040,0x0048,0x0058,0x0068,0x0078,0x0080,0x0080,0x0090,0x0090,0x00A0,0x00A8,0x00B0,0x60A8,0x40A0,0x40A0,0xA0B0,0xC0B8,0x60B8,0x20B8,0x20C0,0x20D0,0x00D0,0x00D0,0x00D8,0x00D8,0x20E8,0x40E0,0x60E0,0xC0E0,0xC5B9,0x0A63,0x095B,0x0A5B,0x0963,0x095B,0x0A5B,}, +{0x435B,0xE38B,0x439B,0xE58A,0x286B,0x675B,0x673B,0x042B,0x8212,0xC422,0xE41A,0x641A,0xC41A,0xA422,0xA312,0x4522,0x2512,0xC411,0xEC32,0x2A1A,0xEC32,0x0F3B,0x0D1A,0x4F2A,0xEF21,0x0E22,0x0D1A,0x593B,0xD419,0x5422,0xB519,0x0C11,0xEE10,0xF642,0x9C63,0xF018,0x7019,0xB439,0xEC08,0x4E19,0xB529,0x6A00,0x4400,0x6B19,0xFD63,0x7F53,0x9C32,0x9119,0x8808,0x8410,0xED10,0x6A08,0x6400,0x6400,0x8910,0xD118,0xB008,0x3411,0xF308,0xF208,0xF108,0xAE00,0xAF08,0x8D00,0x6B00,0x6800,0x4400,0x6608,0xAA08,0x1011,0x5C32,0xDE42,0x7719,0x3611,0x8F08,0xF310,0x1211,0x1311,0xCE08,0x8D08,0xAB08,0xAE10,0xD008,0x7819,0x5311,0x6408,0x8408,0x4210,0x2118,0x0020,0x0028,0x0030,0x0038,0x0048,0x0058,0x0068,0x0070,0x0078,0x0080,0x0088,0x0090,0x00A0,0x00A8,0x00A8,0x40A8,0x60A8,0x60A8,0x60A0,0x4098,0xA0B8,0xC0B8,0x40C0,0x20D0,0x00D0,0x00D8,0x00D8,0x20D8,0x40D8,0x60E0,0xC0E0,0xC0E0,0x2792,0x0A5B,0x0963,0x095B,0xEA5A,0xEA5A,0xEA5A,}, +{0x228C,0xE2AB,0x80A2,0x8592,0x8B74,0xAB64,0x664B,0x663B,0xC633,0x452B,0xA41A,0xC51A,0xC62A,0xA622,0x8422,0x241A,0x0212,0x8319,0x6311,0xC429,0xC621,0xE619,0x6C2A,0x0B12,0xAE21,0xEF32,0x7D64,0xD722,0xAE3A,0xB43A,0x2C09,0x4E11,0x9432,0x1522,0x8A00,0x8600,0xCC10,0x542A,0x9A2A,0x3632,0xEB08,0x6400,0xE708,0xBC5B,0x3F74,0x1C4B,0x3319,0x4600,0x0300,0xC810,0x8700,0x6708,0x8900,0x8808,0x8908,0xAD08,0xCF10,0x3519,0x5411,0x3309,0x3309,0xF208,0xCF08,0xCB00,0x8A00,0x6800,0x4400,0x6300,0x4608,0xEF10,0x9B21,0x9919,0x3611,0xB108,0xD210,0xB108,0xB008,0x5919,0xF108,0x8D08,0xF108,0xF010,0x1309,0xFC29,0x9821,0xC900,0x6308,0x6210,0x4210,0x0018,0x0028,0x0030,0x0038,0x0048,0x0050,0x0060,0x0070,0x0070,0x0078,0x0080,0x0088,0x0090,0x0098,0x00A0,0x00A0,0x20A0,0x40A0,0x40A0,0x40A0,0x4098,0x43B9,0x80C8,0x40C8,0x20D0,0x20D8,0x20E0,0x20E0,0x80E0,0xA0D8,0x20E1,0x00D9,0xA972,0x095B,0x095B,0x0963,0x095B,0x095B,0x095B,}, +{0xA49C,0x44AC,0x669B,0x2B8B,0xE76A,0x8452,0x6953,0x6933,0x693B,0x0733,0xE72A,0x6522,0x4622,0x4622,0x0522,0x051A,0xE319,0x8219,0x6011,0x4111,0x2111,0x8421,0x0419,0x0811,0x755C,0x5E7D,0x7A3B,0xF522,0x7722,0x8E09,0x8A11,0x9332,0xB119,0xED00,0xA900,0xA800,0x9853,0x7F6C,0x9F4B,0xF729,0xCC08,0xA508,0x194B,0x5C5B,0x7329,0x2700,0x4400,0x4300,0xCB08,0xCD08,0xCA08,0xCC08,0xCE08,0xCE10,0xCD08,0xCF10,0xF010,0x5611,0x9921,0x7719,0x3511,0xF408,0x1309,0xD108,0x8B00,0x6700,0x6400,0x4300,0x6500,0xAF10,0x3619,0xD108,0xD308,0xB108,0xD308,0xB110,0xCE08,0x7B21,0xF408,0xAF08,0xF510,0x9619,0x5519,0xFC29,0xB821,0x0D11,0xA808,0x6210,0x2110,0x2218,0x0020,0x0030,0x0038,0x0040,0x0050,0x0058,0x0060,0x0068,0x0078,0x0080,0x0080,0x0088,0x0090,0x0098,0x0098,0x0098,0x0098,0x2090,0x2098,0x4090,0x60A0,0xC0C0,0x80C8,0x60D0,0x60D8,0x60D8,0x40E0,0x80D8,0x00D9,0x20E1,0x84C1,0x0963,0x095B,0xE95A,0x095B,0xE95A,0x095B,0x095B,}, +{0x0594,0x83AC,0x82AB,0x438A,0xE579,0x4773,0xAA5B,0x8A4B,0xC93A,0x0B33,0x8932,0x0622,0x0522,0xE529,0xE529,0xE521,0x052A,0xE421,0xC221,0x8119,0x2119,0xE018,0x892A,0x1965,0x7F7D,0x5E5C,0x9C43,0x5722,0x0C11,0x6709,0x9222,0x131A,0xCF09,0xF109,0xD509,0x3B64,0x3F8D,0x7E7C,0xDA4A,0xF010,0x6500,0x6C21,0x5021,0x8800,0x6700,0x4500,0x4400,0xCB10,0xCE08,0x1119,0x0D09,0xCD08,0xAD08,0xEF10,0xEF10,0xCF10,0x3321,0x3619,0x5719,0x3819,0x9819,0xBA19,0x9919,0xD000,0x6A00,0x6600,0x8400,0x6500,0x6800,0x8C08,0xCE08,0xCE08,0xAE10,0xAC08,0xD008,0xAE08,0x6E08,0x9B21,0xD108,0x9108,0x3619,0x1A32,0x3521,0x7A19,0x5419,0xAC10,0xCF10,0x8710,0x4310,0x0118,0x0020,0x0028,0x0038,0x0040,0x0048,0x0050,0x0060,0x0068,0x0070,0x0078,0x0078,0x0080,0x0088,0x0088,0x0088,0x0088,0x0090,0x2088,0x2090,0x2090,0x6090,0x21C9,0xE0D0,0x80D0,0x60D8,0x80E0,0xA0D8,0xC0D8,0x40D9,0x60D9,0x279A,0x0A63,0xE95A,0xEA5A,0xE95A,0xE95A,0x095B,0x0953,}, +{0xE7A3,0x46A4,0x6593,0x657A,0x0662,0x6752,0xE842,0x2A4B,0xCA42,0xCC3A,0x6932,0x482A,0xC629,0xA519,0xC429,0xC529,0xA531,0xA429,0xA221,0x8221,0x2119,0x683A,0xD25B,0xB332,0x2F19,0x8A08,0x4500,0x2100,0xE811,0xD12A,0xB41A,0x131A,0x531A,0x5422,0xBB5B,0xDB63,0xF539,0x6C00,0xA900,0xAC08,0x8908,0x2300,0x6500,0x8708,0x8900,0x6900,0xCA08,0xCB10,0xEE10,0xEF08,0xF010,0xED10,0xD010,0x1011,0x0F11,0xF210,0x3411,0x5811,0x7A19,0xDC21,0xFD21,0xBB21,0x1511,0xB008,0xAC00,0x8900,0x6700,0x8700,0x8A08,0xCD08,0xCF10,0xAE08,0xCD08,0x1209,0x8B00,0x6A08,0x4E08,0x5711,0x9000,0x6C00,0xB010,0x9821,0x0D11,0xF108,0xAB10,0x6B08,0xD210,0xCC10,0x6408,0x2208,0x0008,0x0020,0x0028,0x0030,0x0040,0x0048,0x0050,0x0058,0x0068,0x0068,0x0068,0x0070,0x0078,0x0078,0x0080,0x0080,0x2080,0x6080,0xC078,0x6371,0x676A,0x04BA,0x21D9,0xA0D8,0x60D8,0xA0E0,0xE0E0,0x20D9,0xA0D9,0x81D9,0xC972,0x095B,0xEA5A,0xE95A,0xE952,0xE95A,0xE95A,0xE95A,}, +{0x8FEC,0x6FCC,0x6C93,0x4872,0xE659,0x8741,0x4A4A,0xE739,0xC739,0x0842,0x0932,0x8932,0x282A,0x0722,0x8529,0xC529,0x8339,0x6331,0x0231,0xE128,0xC020,0x6020,0x4010,0x2018,0x0010,0x0008,0x2008,0xAB1A,0xF63B,0x153B,0x162B,0x3322,0x321A,0xAF19,0x3011,0xCD08,0xEA08,0x3011,0xEF10,0x8800,0xA500,0xEA08,0xC808,0xC808,0xA900,0x6C00,0x8B08,0x2D19,0xCD08,0xF210,0x1111,0x1011,0x3219,0x1419,0x3009,0x3411,0xBA21,0xDA19,0x1C32,0xFC29,0xDC21,0x5811,0xF608,0xF208,0xB008,0xAE08,0x8B00,0x8900,0x8D00,0xCD10,0xF010,0xAF08,0x1511,0x7911,0x8D00,0x4A00,0x4A00,0x8D08,0x4D08,0x6B08,0x8B10,0xF118,0xA908,0x6808,0x4800,0x8D08,0x8E08,0xCF10,0x8708,0x2300,0x0000,0x0000,0x0000,0x0010,0x0020,0x0030,0x0038,0x0048,0x0050,0x0058,0x0058,0x2060,0x2068,0x6160,0x8260,0xC658,0x6951,0x686A,0x0A6B,0xEA62,0x2A6B,0x65AA,0x00D1,0xC0D8,0xE1E0,0xE0E0,0x20D9,0x80D9,0xE0D9,0x04BA,0xEA5A,0xE95A,0xE95A,0xEA52,0xE952,0xE95A,0xE95A,0xE952,}, +{0x89DB,0xABC3,0x6A9B,0xA972,0xC751,0x6531,0x6541,0x8541,0xC641,0x074A,0xAB42,0x6A32,0x6A32,0x892A,0x492A,0xC521,0xA429,0x2331,0xA138,0xC130,0x6028,0x4018,0x2018,0x0018,0x0008,0x8208,0xD443,0x3E75,0x9D6C,0x7633,0x2F2B,0xCB22,0x6C22,0xEB11,0x8D11,0x9119,0x5111,0xEB08,0x0C11,0x0C11,0x0C11,0x0C11,0x0A11,0x0A19,0xCA08,0xAC08,0xCE08,0x3019,0xF110,0x1309,0x5411,0x7419,0x7619,0x7611,0xB719,0x9621,0x3C22,0xD919,0x9A19,0x7919,0x1511,0x1411,0x3711,0xF408,0xD108,0xD108,0xAE00,0xAA08,0x8B08,0xAB08,0x8D08,0xB108,0x7919,0x3711,0x8E00,0x6B00,0x2700,0x4A00,0x4E08,0x6C08,0x4508,0x6800,0x6700,0x4708,0x4400,0x6E08,0x8F08,0x8808,0x8900,0x4908,0x2100,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2008,0x2010,0x2010,0x6218,0xC318,0xE720,0x2A21,0xAC29,0x2C42,0x2B6B,0x4B6B,0x4A6B,0x4A63,0xA5A2,0x40D1,0xE0E0,0x00E1,0x40E1,0x80E1,0x20E2,0x20DA,0x8892,0xE952,0xE95A,0xE95A,0xE95A,0xE952,0xE95A,0xC952,0xC952,}, +{0xCBF3,0x4DF4,0x6FE4,0xADC3,0x48A2,0x8689,0x4471,0x8479,0x8471,0x2682,0x8872,0xAA52,0x6932,0x892A,0xC619,0x6521,0x8219,0x6139,0x4239,0xA130,0x6020,0x8118,0xA018,0x4008,0x292A,0xF553,0xB753,0x3132,0xC700,0x0511,0x492A,0x8D22,0x5222,0x5122,0x1122,0x7021,0xD119,0xD421,0x2E11,0x4D19,0x4D19,0x0911,0xEA10,0xE910,0x0A19,0xED10,0x1111,0x1111,0x5411,0x5611,0x5311,0x7419,0x1B32,0x3B2A,0xDA29,0xFA29,0xB919,0xB921,0x3519,0xF408,0xD508,0xF210,0xD408,0xF400,0xF610,0xD108,0xAF00,0xEE08,0xCA08,0x6908,0x8D00,0xB108,0x1919,0xF508,0x9008,0x4B00,0x4A00,0x6B00,0x8C00,0x4900,0x4608,0x4600,0x4608,0x2408,0x4200,0x4D00,0xB108,0x6808,0x4908,0x6808,0x4200,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2008,0x4010,0x4108,0x6410,0xE608,0x2921,0x8B29,0x2B42,0xAB5A,0x4B63,0x4B6B,0x4B6B,0x2A63,0xC5A2,0xA0D1,0x40E1,0x40E1,0x80E1,0xC0D9,0x60DA,0x24C2,0xCA5A,0xE95A,0xE95A,0xE95A,0xE95A,0xE95A,0xC95A,0xC95A,0xC952,}, +{0x12FD,0x11FD,0xAFF4,0xACD3,0x27CA,0xA4D1,0xA5E1,0x26E2,0xC5D9,0xE5B9,0x458A,0x0562,0xA341,0x8229,0x8221,0x8121,0x6019,0xC110,0x4010,0x4008,0x4108,0x8208,0x8210,0x2942,0xA621,0x8308,0x6100,0xC408,0x2711,0x0811,0xCA19,0xED21,0x2F1A,0x522A,0x8D19,0x3332,0x9832,0x9021,0x9021,0xAF21,0x4E19,0x2D11,0xEC18,0xC710,0x2A19,0x3011,0x3111,0x1319,0x5511,0x1409,0xD821,0xB829,0x192A,0x3B32,0xFA21,0xD921,0x5419,0x1411,0xB208,0xD008,0xF008,0xF208,0x1411,0x3719,0x1409,0xF408,0x1311,0x5411,0xCD00,0xA908,0xAA00,0xF408,0xF810,0x8F00,0x9100,0x6F08,0x8C08,0xF208,0xCF08,0x4908,0x4708,0x2708,0x2708,0x2400,0x2400,0x2700,0x4B08,0x8F08,0x6708,0x4300,0x2100,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2100,0x4008,0x6008,0x6208,0x6608,0xCB10,0x8A31,0xEB39,0x8A52,0x4B6B,0x6B6B,0x4B73,0x4B6B,0x4A63,0xE5AA,0xE0D9,0x80E1,0xA0E1,0xE0D9,0x20DA,0xA0DA,0x8892,0x095B,0xE95A,0xE95A,0xC962,0xC95A,0xEA5A,0xC852,0xC95A,0xE852,}, +{0x75FD,0x12FD,0x6FFC,0x6AFB,0xA8FA,0xA8FA,0xA8FA,0x07FA,0x63E9,0xE1C8,0x21B9,0xE1A0,0x0289,0xE060,0x8048,0x4040,0x2028,0x0010,0x0008,0x0008,0x2000,0x0000,0x0000,0x0000,0x0000,0x4008,0xA310,0x2511,0x6819,0x6B19,0x0709,0x4811,0xCD21,0x8C21,0xF229,0x3332,0x332A,0xF229,0x132A,0xB021,0x9129,0x6F19,0x2D11,0x0C11,0xE908,0x0D19,0x1019,0x7521,0x7619,0x7419,0xD821,0xB721,0x9621,0xD821,0xB821,0x5611,0x1311,0xD008,0xD010,0xB008,0x9008,0xCE10,0xEF10,0x3411,0x1411,0x5511,0xFC29,0x7519,0xF008,0xAD08,0x8C08,0xD008,0xB008,0x6E00,0x8E00,0x6E08,0xAF08,0x1511,0xD108,0x6A00,0x4B00,0x2800,0x2608,0x2500,0x2600,0x2800,0x4500,0x4808,0x6B00,0x2200,0x2100,0x0000,0x0000,0x0000,0x2008,0x2000,0x2100,0x2100,0x4108,0x6008,0x8308,0x6708,0x0C11,0xCA31,0x694A,0x0B63,0x6B6B,0x4B73,0x4B6B,0x4B6B,0x4A63,0x25B3,0x40DA,0x00EA,0x00E2,0x40DA,0xC0DA,0x83BA,0xC95A,0xE95A,0xE95A,0xE95A,0xC952,0xC85A,0xC952,0xC952,0xA95A,0xC952,}, +{0xF2FC,0x4EFC,0x8BFB,0x4AFB,0x2AFB,0x4BFB,0xC8FA,0xE4F9,0x62F1,0xE1E8,0xA0D0,0x80B0,0x60B0,0x4098,0x0078,0x0058,0x0048,0x0038,0x0020,0x0010,0x0000,0x0000,0x0000,0x0000,0x0000,0x0008,0x0008,0x0008,0x2008,0x8308,0xE710,0x8508,0x0609,0x8D11,0x3222,0xF231,0xD221,0x5232,0xF129,0xF329,0x9021,0x9019,0x4F19,0x0E19,0xEB08,0xC810,0x0F21,0x1119,0xB521,0xB619,0x1119,0x9621,0x1109,0x1111,0x3311,0x3411,0xF408,0xD108,0xD008,0xD108,0x8F08,0x8D00,0xCC10,0x3319,0x3411,0xFC29,0x9A21,0x3519,0x1311,0xAE08,0x8D08,0x8B08,0x8908,0x6908,0x6B08,0x8D08,0x8F08,0xF810,0x9308,0x4B00,0x2B00,0x2800,0x2608,0x4400,0x2400,0x2700,0x2500,0x4608,0x4A08,0x4400,0x2100,0x2000,0x2000,0x2000,0x2000,0x2000,0x2308,0x4100,0x4208,0x8210,0xA508,0x8810,0x2C19,0x0A42,0xCA5A,0x4B6B,0x6B73,0x4B6B,0x6B6B,0x4B6B,0x4A63,0x43BB,0xA0E2,0x60EA,0x60E2,0xA0DA,0xE0D2,0xC882,0xEA5A,0x095B,0xE95A,0xE95A,0xC95A,0xE952,0xC952,0xC952,0xC95A,0xA952,}, +{0x4EFC,0xABFB,0x69FB,0x6AFB,0x8CFB,0x2AFB,0x0AFB,0x87FA,0x63F9,0xC0F0,0xC0E0,0xA0D8,0x80C8,0x60A8,0x2098,0x2080,0x0068,0x0050,0x0048,0x2028,0x2008,0x2008,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2000,0x0719,0x4A19,0x4D11,0xF221,0xF021,0x8F21,0x9029,0x6E21,0xAF21,0xD121,0x5119,0xED10,0xCA08,0xA808,0x0C19,0x5221,0x5111,0x5211,0xCE08,0x1011,0x1011,0xF008,0xCE08,0xF108,0xF008,0x1211,0xAF00,0x8D00,0x8E00,0xD108,0xF210,0x9811,0x9921,0x1619,0xF410,0xD208,0xB108,0x8E00,0x6A00,0x2800,0x4600,0x4A08,0x6D00,0x9108,0xD508,0x9100,0x4B00,0x2700,0x2508,0x4408,0x2208,0x2200,0x2508,0x4400,0x2508,0x2400,0x2200,0x2200,0x2100,0x4100,0x2008,0x2000,0x4100,0x4308,0x4200,0x4300,0x8408,0xC608,0xC810,0x4A21,0x494A,0x0B63,0x8C73,0x6B6B,0x6B6B,0x6A6B,0x4B6B,0x4A6B,0x81CB,0x00E3,0xE0E2,0xE0DA,0x40D3,0xA5AA,0x0A5B,0xE95A,0xE95A,0xE95A,0xC95A,0xE952,0xC952,0xC952,0xC95A,0xA952,0xC952,}, +{0xECFB,0x69FB,0x4AFB,0xACFB,0x4AFB,0x6BFB,0x4BFB,0xE9FA,0x05FA,0x42F9,0x00F9,0xC0F0,0x80E0,0x80D0,0x80C0,0x60A8,0x4098,0x4088,0x4070,0x4050,0x6020,0x4110,0x2100,0x2100,0x0100,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2100,0x0819,0x6A19,0x6D19,0xEC10,0xEB18,0x2E21,0x2D19,0x4E19,0xED10,0xCE18,0xAA10,0x8808,0x8710,0xC810,0x2B19,0x2F19,0xCA00,0xCE08,0xAD08,0xCA08,0xEC10,0xCC08,0xAD08,0xAC10,0x8D00,0xB000,0xD408,0xD508,0xF210,0x1211,0xD310,0xF510,0xD208,0x9108,0xB008,0x8E08,0x8B08,0x4708,0x4700,0x4900,0x6E00,0x7008,0xB200,0x4E00,0x2900,0x2500,0x4200,0x2100,0x2100,0x2200,0x2300,0x2300,0x2400,0x4400,0x2200,0x2200,0x0300,0x2300,0x4108,0x2200,0x4208,0x4200,0x2200,0x4500,0x8508,0xC608,0xE818,0x8A29,0x8A5A,0x4C6B,0x6C73,0x8B6B,0x6B6B,0x6B6B,0x4B6B,0x6983,0xE0D3,0x60DB,0x40DB,0x20D3,0x03BB,0xC972,0x095B,0xE95A,0xE952,0xE95A,0xC952,0xC952,0xC952,0xA952,0xA952,0xA952,0xA952,}, +{0xCBFB,0x4AFB,0x8AFB,0x8AFB,0x6AFB,0x8CFB,0x2BFB,0xE9FA,0x66FA,0xE4F9,0x83F9,0x42F9,0x01F9,0x01F9,0xE1E8,0xA0D8,0x80C8,0x80B8,0x60B0,0x8180,0xA130,0xA218,0x8320,0x4320,0x2218,0x0110,0x0008,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2010,0x4310,0x2310,0xA818,0xA818,0x0919,0x8610,0xCC18,0x8C10,0x8B10,0xAB08,0x8708,0x6508,0x8408,0xE710,0xEB18,0xED08,0xAA08,0x8808,0xEC08,0xEF08,0xAF08,0x8F08,0xCE00,0xCF08,0xF408,0xB408,0xB008,0x8F08,0xB110,0xF408,0xB108,0x8F00,0x8F08,0x6B08,0x4800,0x2700,0x2800,0x4C00,0x6D00,0x4A00,0x6B00,0x4900,0x2600,0x0300,0x2100,0x2100,0x2100,0x2100,0x2200,0x2200,0x2300,0x2300,0x2200,0x2200,0x0500,0x2500,0x4208,0x2100,0x4100,0x2300,0x2300,0x4600,0xA808,0xE710,0x4821,0xEB39,0xAB52,0x4B6B,0x8B73,0x6C6B,0x6B6B,0x4B6B,0x4A6B,0xE8A3,0x40D4,0xE0D3,0xA0D3,0x41CB,0xE882,0x0963,0x095B,0x0953,0xEA5A,0xE95A,0xE95A,0xC952,0xC952,0xA952,0xA952,0xA852,0xA952,}, +{0xCBFB,0x49FB,0x49FB,0x6AFB,0x8BFB,0xABFB,0x4BFB,0x0AFB,0x87FA,0x05FA,0x04FA,0x05FA,0x25FA,0xE5F9,0x62F9,0x21F9,0xE1F0,0x01E9,0xE1D8,0xE1A8,0x6479,0x4481,0xC370,0x8350,0x2240,0x2128,0x2110,0x2108,0x2000,0x0000,0x0000,0x0008,0x0000,0x0008,0x0018,0x0020,0x0020,0x0020,0x0018,0x0018,0x0018,0x0010,0x0110,0x6518,0x8A18,0xAD18,0xAC18,0x8A10,0x6708,0x8600,0x8400,0xA710,0xCD08,0xAD08,0xCE08,0x0F09,0xF008,0xD208,0x6F08,0xAE08,0x8D08,0xB010,0x9110,0xB008,0x8F00,0x8F08,0x6F08,0x8C08,0x8C08,0x4600,0x2400,0x2500,0x4600,0x4A00,0x4F00,0x6D00,0x4B00,0x0700,0x2800,0x2500,0x2400,0x0200,0x2100,0x0200,0x2008,0x2100,0x0200,0x0200,0x0300,0x2200,0x2200,0x0500,0x4500,0x2300,0x2100,0x2300,0x2500,0x4500,0x6600,0xAA00,0x0811,0x6929,0x4B42,0xCB5A,0x4B63,0x8C6B,0x6B6B,0x6B73,0x4B6B,0x4B6B,0x67B4,0x80D4,0x40CC,0x01CC,0x2793,0xE95A,0x0A63,0xEA62,0x095B,0xCA5A,0xC95A,0xC952,0xC952,0xC952,0xA852,0x8852,0xA852,0xA952,}, +{0xCBFB,0x28FB,0x49FB,0x6AFB,0x8BFB,0xABFB,0x8CFB,0xEBFA,0x87FA,0x66FA,0x05FA,0x88FA,0x67FA,0x46FA,0x26FA,0xE5F9,0xA4F9,0x83F9,0x62F1,0xA3E9,0x86EA,0x84C9,0x04B1,0xC490,0x8468,0x2348,0x2328,0x2220,0x0018,0x2010,0x2010,0x0010,0x0010,0x0018,0x0040,0x0038,0x0030,0x0028,0x0028,0x0028,0x0020,0x0018,0x0020,0x0020,0xA828,0x8A18,0xCC20,0x8B18,0xAB10,0x8900,0x4700,0x4508,0x8908,0xAE10,0x1111,0x1109,0xAF08,0xAC00,0xAF08,0x8C00,0x6D00,0x6A00,0x8D10,0xAF08,0x8C00,0x8B08,0x4A00,0x2900,0x2800,0x2500,0x0400,0x0400,0x2600,0x2B00,0x2D00,0x2B00,0x2900,0x0700,0x0700,0x2400,0x2400,0x4200,0x2200,0x0100,0x0000,0x0000,0x2100,0x0200,0x2300,0x2300,0x4300,0x2500,0x2500,0x2300,0x2400,0x2400,0x4700,0x4608,0x8808,0xA908,0x4819,0xE939,0x8B52,0x0B5B,0x4C6B,0x8C73,0x6C6B,0x6B6B,0x6B6B,0x4A6B,0xA6C4,0xE1DC,0x81CC,0x6793,0x0A63,0xEA62,0xE95A,0xEA5A,0xE95A,0xE95A,0xE952,0xC95A,0xC952,0xA952,0xA952,0xA952,0xA852,0xA852,}, +{0xCBFB,0x69FB,0x49FB,0x6AFB,0x8AFB,0xABFB,0xACFB,0x2BFB,0x69FA,0x67FA,0x67FA,0x66FA,0x87FA,0xC9FA,0xA7FA,0x87FA,0x46FA,0x46FA,0x46FA,0xC7FA,0x88FA,0x26F2,0xA5D9,0x65C1,0x27A1,0xA678,0x8468,0x8360,0x0230,0x0118,0x0118,0x2010,0x0018,0x0048,0x0060,0x0060,0x0050,0x0048,0x0040,0x0040,0x0028,0x0030,0x0038,0x0038,0x0038,0x2238,0x6830,0x8A20,0x8D20,0x6A10,0x6900,0x6600,0x2500,0xA910,0x0F19,0x1109,0x2800,0x4600,0xEF10,0xF008,0x6D00,0x6500,0x4708,0x6A08,0x6700,0x6700,0x6600,0x2800,0x2700,0x2600,0x2500,0x2400,0x2400,0x6800,0x4A00,0x4900,0x2600,0x2700,0x2500,0x2500,0x2400,0x2200,0x0100,0x2000,0x0000,0x0000,0x0000,0x2100,0x2200,0x4100,0x4208,0x4300,0x4400,0x2400,0x2500,0x2600,0x6600,0x8700,0xA800,0x0909,0xC729,0x894A,0x0A5B,0x2C6B,0x8C73,0x8C73,0x6C6B,0x8B6B,0x4B63,0x2B6B,0xC8C4,0xC5CC,0x8893,0xEA62,0x095B,0x0A63,0xEA62,0xE95A,0xC95A,0xC952,0xC952,0xC952,0xA95A,0xA952,0xA952,0xA852,0x8852,0xA852,}, +{0xEBFB,0x48FB,0x07FB,0x48FB,0x8AFB,0x8BFB,0x8BFB,0x4BFB,0xCAFA,0xC7F1,0x46FA,0xA7FA,0xA8FA,0xC9FA,0xC8FA,0xE9FA,0xC8FA,0xA8FA,0xE8FA,0x49FB,0x0AFB,0x2AFB,0xA9FA,0x08E2,0xA7C9,0x26C1,0xA8C1,0xA790,0x0358,0x0140,0x0230,0x0128,0x0028,0x0050,0x0080,0x0078,0x0078,0x0058,0x0058,0x0050,0x0038,0x0058,0x0058,0x0058,0x0060,0x0050,0x0148,0x8740,0x6838,0x6820,0x6708,0x6708,0x4500,0x4300,0x6508,0x8708,0x2300,0xAB08,0xEF08,0xD008,0x4800,0x4400,0x4200,0x2300,0x6500,0x4600,0x4500,0x4600,0x4600,0x2500,0x2500,0x2400,0x2300,0x2300,0x4300,0x2500,0x2300,0x2400,0x2500,0x2600,0x0300,0x0100,0x0000,0x2000,0x2000,0x0000,0x2000,0x2000,0x4000,0x2000,0x4100,0x4200,0x4400,0x2500,0x4500,0x4500,0x6600,0xA608,0xC608,0x6909,0x6632,0x475B,0xAA6B,0x8C73,0x8C73,0x8C6B,0x6C6B,0x6B6B,0x6B63,0x4B6B,0x6A73,0x4A7B,0x0A63,0x0A63,0x0A5B,0xEA5A,0xE95A,0xE95A,0xC95A,0xC952,0xE952,0xA952,0xC952,0xA952,0xA952,0x8952,0x8952,0x8852,}, +{0x8AFB,0x48FB,0x07FB,0x28FB,0x49FB,0x6AFB,0x6AFB,0x6AFB,0x0CFB,0x48EA,0x68FA,0x48FA,0xA8FA,0xC8FA,0xE9FA,0x09FB,0x09FB,0x09FB,0x2AFB,0x2AFB,0x4BFB,0x8CFB,0x4BFB,0xEBFA,0x47F2,0x6AF2,0x67D1,0x29C1,0x6598,0x2480,0x0360,0x0348,0x0248,0x0058,0x0060,0x0060,0x0078,0x0078,0x0070,0x0060,0x0060,0x0070,0x0080,0x0080,0x0090,0x0080,0x0080,0x0068,0x2260,0x2250,0x6328,0x6410,0x2308,0x2208,0x0100,0x2100,0x4308,0xA910,0xCD10,0xAB10,0x2400,0x2500,0x2400,0x4500,0x6508,0x6700,0x4700,0x4700,0x4600,0x4500,0x0400,0x2400,0x0200,0x0000,0x2100,0x4500,0x2400,0x2400,0x0500,0x0500,0x2200,0x0000,0x0000,0x2000,0x2000,0x2000,0x2000,0x2008,0x2000,0x2100,0x4100,0x4200,0x4400,0x4600,0x4500,0x6500,0x8608,0xC508,0x2509,0xC711,0xE53A,0xE45B,0x086C,0x2A74,0xEC73,0x8C6B,0x6C6B,0x6B6B,0x4B6B,0x2B6B,0x2A63,0x2A6B,0x0A63,0xEA62,0xEA62,0xEA5A,0xE962,0xC95A,0xC95A,0xC95A,0xC952,0xA952,0xA952,0xA852,0xA952,0xA852,0x894A,0xA852,}, +{0x8CFB,0xAAFB,0x48FB,0x07FB,0x07FB,0x4AFB,0x6AFB,0x8AFB,0x4AFB,0x6CFB,0xAAFA,0x08FA,0x28FA,0xC9FA,0x09FB,0x29FB,0x29FB,0x4AFB,0x6AFB,0x8CFB,0xEEFB,0xACFB,0x6CFB,0x8DFB,0xE9FA,0xEBFA,0xE8F1,0x87E1,0x06D9,0xA6C0,0x45A0,0x4488,0x4390,0x2270,0x2178,0x0068,0x0070,0x0070,0x4180,0x6090,0x40A0,0x40A0,0x40A8,0x40B0,0x40B8,0x40B8,0x40B0,0x2098,0x0090,0x0080,0x0060,0x2038,0x2118,0x2210,0x0208,0x2200,0x2208,0x2308,0x4500,0x2600,0x2600,0x4700,0x4708,0x4600,0x4500,0x6700,0x4900,0x6900,0x6800,0x4600,0x4400,0x2400,0x2100,0x0000,0x0000,0x2108,0x4300,0x4500,0x2400,0x2300,0x2200,0x2000,0x0000,0x2000,0x2000,0x2100,0x2100,0x4108,0x4208,0x4100,0x4100,0x4208,0x6308,0x6408,0x6408,0x8608,0xA608,0x0609,0x6509,0x2612,0x2433,0x625C,0xA464,0x676C,0x4A74,0xCB6B,0x8B6B,0x6B6B,0x4B6B,0x2B63,0x2B63,0x2A5B,0x2A63,0x0A5B,0x095B,0x095B,0xEA5A,0xEA5A,0xC95A,0xC952,0xC952,0xA952,0xA952,0xA852,0x8952,0x8852,0x884A,0x884A,}, +{0x8CFB,0x4BFB,0x48FB,0x28FB,0x27FB,0x29FB,0x49FB,0x69FB,0x8BFB,0x6CFB,0x0CFB,0xCDFA,0x4AFA,0xE9F1,0x49F2,0xE9FA,0x09FB,0x4AFB,0xADFB,0xADFB,0xCDFB,0x6BFB,0x8CFB,0x6DFB,0x0AFB,0x2BFB,0xEBFA,0x27FA,0xC6F9,0x04E9,0xC4E0,0x84C8,0xC5D0,0xC4C0,0xA3B0,0x6298,0x6198,0x6198,0x03B9,0x01C9,0xC0C8,0xA0D8,0xA0C0,0xA0D8,0x80D0,0xC1E0,0x80E0,0x60D8,0x40C8,0x60B8,0x40A0,0x0080,0x4150,0x2338,0x2418,0x2310,0x2118,0x2210,0x2210,0x4408,0x4708,0x6700,0x6800,0x4600,0x4600,0x6500,0x6608,0x8908,0x8800,0x6700,0x4400,0x2300,0x2100,0x0000,0x0000,0x0000,0x0000,0x4200,0x2300,0x4300,0x2100,0x0000,0x0000,0x2000,0x2008,0x2208,0x4208,0x4208,0x4308,0x4308,0x4300,0x4200,0x6208,0x6308,0x8408,0xA508,0xC608,0x2511,0xA409,0x450A,0x612B,0x604C,0xE15C,0xE364,0x8764,0x4A74,0xCB6B,0x6B6B,0x4B6B,0x2A6B,0x2A63,0x0A63,0x0A63,0x0A5B,0x095B,0x0963,0xC95A,0xE952,0xC952,0xA952,0xA952,0xA852,0xA952,0x8952,0x8852,0x884A,0x884A,0x8852,}, +{0x4BFB,0x0BFB,0x2AFB,0x09FB,0x69FB,0x07FB,0xE7FA,0x6AFB,0x4AFB,0x4BFB,0x4CFB,0x2DFB,0x0DFB,0x0CF2,0xA9E9,0xE8F1,0x69FA,0x2AFB,0xADFB,0x8CFB,0x4BFB,0x4AFB,0x6CFB,0x4BFB,0x0AFB,0x2BFB,0xEBFA,0x48FA,0x47FA,0xC5F9,0xA3F9,0x43F9,0xA3F1,0xA5F1,0x22E1,0x02D9,0xC1D0,0xC1D0,0x62E1,0xA3E9,0x22E9,0x84E9,0x42E9,0x42F1,0x01E9,0x42F1,0x01F1,0x22F9,0xE1E8,0xE2E8,0xE1E0,0x80B8,0x2078,0x4268,0x6528,0x4528,0x2228,0x2428,0x0220,0x2318,0x4318,0x4310,0x6410,0x6310,0x6410,0x4308,0x2400,0x4508,0x6608,0x4308,0x4400,0x2200,0x2200,0x2100,0x2000,0x2000,0x0000,0x0000,0x2008,0x2008,0x2000,0x2000,0x2000,0x4000,0x2108,0x4200,0x4208,0x6208,0x4408,0x6408,0x6408,0x4400,0x6208,0x6210,0x8308,0xC410,0xE508,0x2411,0xC311,0x421A,0x2023,0x003C,0xE054,0x2265,0x046D,0xC774,0x4A74,0x8B6B,0x4B6B,0x2B6B,0x0B63,0x0B63,0x0A63,0x0A63,0xEA5A,0xE95A,0xC95A,0xC95A,0xC952,0xC95A,0xA952,0xA852,0x8952,0x884A,0x6852,0x8852,0x884A,0x884A,}, +{0x08FB,0xEAFA,0xAAFA,0xCAFA,0xCAFA,0xC8FA,0xC7FA,0x08FB,0x09FB,0x09FB,0x2AFB,0x4CFB,0x2EFB,0x0FFB,0xADFA,0x0AF2,0xCAFA,0x31DB,0x0FEB,0xCBFA,0xC8FA,0x2AFB,0x0BFB,0xC9FA,0xE9FA,0xEAFA,0x88FA,0x26FA,0x67FA,0x46FA,0x47FA,0x67FA,0x67FA,0x47FA,0xC5F9,0xA4F9,0x64F9,0x83F9,0xE4F9,0x25FA,0xA3F9,0xE7F1,0x86E9,0xC7E9,0x85F1,0xE7F9,0x85F1,0x85F1,0x63F1,0xA4F9,0xA5F1,0x03C1,0x0070,0xE480,0xC528,0x2240,0x6628,0x2330,0x2530,0x2528,0x0220,0x2210,0x0118,0x0018,0x2218,0x2410,0x2510,0x2608,0x2600,0x2500,0x4500,0x4300,0x4208,0x4100,0x2008,0x2000,0x2000,0x0000,0x0000,0x0000,0x0008,0x2000,0x2000,0x4000,0x4100,0x4200,0x6300,0x4308,0x6500,0x6608,0x8600,0x6600,0x8308,0x8208,0xA208,0xC308,0x0309,0x4301,0xC111,0x4012,0xE01A,0x8033,0x404C,0x004D,0x426D,0x256D,0xC76C,0x2A6C,0x6B63,0x2B63,0x2A63,0x0A63,0x0A63,0x095B,0xE95A,0xCA5A,0xC95A,0xC95A,0xC952,0xC952,0xA952,0xA852,0x8952,0x8952,0x8952,0x884A,0x884A,0x684A,}, +{0x08FB,0xA8FA,0x8AFA,0x89FA,0x69FA,0x47FA,0xE7FA,0x28FB,0xE7FA,0xE8FA,0xE9FA,0x2AFB,0x2CFB,0x2DFB,0x4EFB,0x2DFB,0x2BFB,0x36D3,0xACF1,0xE8F1,0x49FA,0x6AFA,0x69FA,0x48FA,0x27FA,0x88FA,0x47FA,0x88FA,0xA9FA,0x67FA,0xCAFA,0xCAFA,0xC8FA,0xC8FA,0x87FA,0x88FA,0x47FA,0x47FA,0x88FA,0x87FA,0x46FA,0x46FA,0xE5F9,0x28FA,0xC8E9,0xA8E9,0x69D9,0x47C9,0x49D1,0x46C9,0x88B1,0x2979,0x2088,0x2881,0x8F29,0x2548,0x8938,0x4630,0x6918,0x6818,0x4618,0x2318,0x0128,0x0020,0x0018,0x2320,0x2520,0x4718,0x6608,0x6508,0x6508,0x6308,0x4308,0x4208,0x4208,0x2100,0x2000,0x0000,0x0008,0x0000,0x0008,0x2000,0x2008,0x2008,0x4008,0x4208,0x4408,0x6500,0x6508,0x6708,0x8708,0x8708,0x8308,0x8210,0xA308,0xE400,0x0201,0x4209,0xE009,0x2012,0xA01A,0x202B,0xC03B,0x804C,0x2055,0x436D,0x066D,0x886C,0xCA6B,0x4A63,0x2A63,0x0A63,0x0A63,0x0A5B,0xEA5A,0xCA5A,0xCA5A,0xC952,0xC852,0xA95A,0xA952,0xA852,0x8852,0x894A,0x884A,0x694A,0x884A,0x884A,}, +{0xE8FA,0x87FA,0x67FA,0xAAFA,0xA9FA,0x49FA,0x89FA,0x08FB,0xE7FA,0xA7FA,0xC7FA,0xE8FA,0xE8FA,0x0BFB,0x2DFB,0xEAFA,0xEAFA,0x92F2,0xEBF9,0x09FA,0x0AFA,0xEAF9,0xE9F9,0xE8F9,0x48F2,0xA6E9,0xC6E9,0xA9F2,0xAAF2,0xA9F2,0x0CF3,0x0BFB,0x2BFB,0x2BFB,0x09FB,0x0AFB,0xE8FA,0xC9FA,0xC9FA,0x09FB,0xC9FA,0xE8FA,0xA8FA,0x88FA,0x26FA,0x28FA,0xA9F1,0x8AE9,0x4BD9,0x2BB1,0x8E99,0x2D89,0x23A0,0x82A0,0x3772,0xAB60,0x4570,0xA950,0x6540,0x8820,0x8728,0x0248,0x0040,0x0030,0x0020,0x0128,0x2438,0x2620,0x6718,0x6710,0x8610,0x8508,0x6508,0x4508,0x4308,0x2308,0x2100,0x0008,0x0008,0x0008,0x0008,0x0008,0x2008,0x2008,0x4008,0x4108,0x4208,0x4410,0x6508,0x6608,0x6708,0xA608,0xA410,0xA310,0xC308,0x0401,0x2201,0x8101,0xC009,0x2012,0x8022,0x0023,0x8033,0x0034,0xA04C,0x015D,0x036D,0xA66C,0x086C,0x6A63,0x2A63,0x0A63,0x0A5B,0xEA5A,0xE95A,0xE952,0xA95A,0xA95A,0xC852,0xA952,0xA852,0x8952,0x8952,0x8952,0x884A,0x894A,0x684A,0x684A,}, +{0xC7FA,0x86FA,0x65FA,0x68FA,0x89FA,0x89FA,0x8BFA,0xA9FA,0x68FA,0xA7FA,0xC7FA,0xC7FA,0x87FA,0xC8FA,0xA8FA,0xA7FA,0xEBFA,0x6BFA,0x49FA,0x4AFA,0x8DFA,0x4BFA,0xACFA,0x8BFA,0x09F2,0x49F2,0x49EA,0x6AE2,0x2BF3,0x2DF3,0x4DFB,0x2BFB,0xEAFA,0x0AFB,0x4AFB,0x2BFB,0x2AFB,0x0AFB,0x0AFB,0x29FB,0x0AFB,0x29FB,0x09FB,0xA8FA,0x67FA,0x67FA,0x27FA,0xC7F9,0x89F1,0x6BD1,0xAFA9,0x6EB1,0xC8C0,0x40C8,0x6EB9,0x1792,0x20B0,0xEE88,0x0080,0x4558,0xAA50,0x0080,0x0070,0x0068,0x0040,0x0030,0x0040,0x2430,0x4828,0x8B20,0x8720,0xAB20,0x8910,0x6710,0x4610,0x4308,0x2108,0x0010,0x0008,0x0000,0x0008,0x2008,0x2008,0x2008,0x2108,0x2108,0x2210,0x6208,0x6410,0x6410,0x8510,0xA410,0xA308,0xA308,0xC408,0x2301,0x2209,0x8001,0xC009,0x2012,0x801A,0x0023,0x4033,0xA033,0xE03B,0xA044,0xA15C,0x6454,0x065C,0x885B,0x2A5B,0x0A5B,0x0A5B,0xEA62,0xEA5A,0xC95A,0xA95A,0xA95A,0xA952,0x8952,0xA952,0x8852,0x884A,0x8852,0x8852,0x694A,0x684A,0x4852,}, +{0xC6FA,0xA7FA,0x87FA,0x46FA,0x46FA,0xC9FA,0x89FA,0x89FA,0x69FA,0x69FA,0xA8FA,0x87FA,0xE8FA,0xA7FA,0xC7FA,0xA8FA,0xA8FA,0x88FA,0x67FA,0xA9FA,0xAAFA,0xA9FA,0xCAFA,0xA9FA,0xC6F9,0x2BFB,0x28FA,0xC6F1,0x6AF2,0xA8E9,0x0AD2,0x6AD2,0x0AC2,0x07D2,0xEBE2,0x0AEB,0x2AF3,0x88F2,0xA8FA,0x06EA,0xA8FA,0xC9FA,0xC9F2,0x2AEA,0xC8E1,0xEBE9,0x08F2,0x07F2,0x88F1,0xA7F1,0xC9E9,0xAAE1,0x89F1,0xA0F0,0xA0E8,0x14CA,0xA1E0,0xC9C0,0x85C0,0x40A0,0x8678,0x6498,0x2188,0x2098,0x0078,0x0048,0x0048,0x0030,0x2240,0x8940,0x8838,0xCC30,0xAC20,0x6928,0x6528,0x2220,0x0018,0x0018,0x0010,0x0010,0x0010,0x0010,0x2008,0x2108,0x2208,0x4108,0x4108,0x4108,0x6208,0x8208,0x8308,0xA308,0xC308,0xC408,0x0309,0x2201,0x4109,0x8009,0xC009,0x2012,0xA01A,0x0023,0x002B,0x402B,0x803B,0xE03B,0x4044,0x224C,0x0454,0xC653,0x685B,0x0A63,0xEA5A,0xEA5A,0xC95A,0xE95A,0xC95A,0xA952,0xA852,0xA952,0xA952,0x884A,0x8852,0x684A,0x884A,0x6852,0x684A,0x484A,}, +{0xA7FA,0xA7FA,0x46FA,0x04FA,0x45FA,0x46FA,0x67FA,0x67FA,0x88FA,0x68FA,0x69FA,0x88FA,0xC9FA,0xA8FA,0x0AFB,0x09FB,0xC9FA,0xE9FA,0xE9FA,0x0AFB,0xEBFA,0x0AFB,0x6BFB,0xEAFA,0x0AFB,0xADFB,0xEAFA,0xC9FA,0x28FA,0x24F1,0x89D1,0xEBC1,0x8AB1,0xAAA1,0x2CB2,0x2CBA,0x8DCA,0x49DA,0x2AD2,0x89B1,0x49C1,0xECB9,0xABC1,0x0FE2,0x2DF2,0x8FF2,0x0BFB,0x2CFB,0x69FA,0x89FA,0x26FA,0x07FA,0x68FA,0xC5F9,0xA4F9,0xA8F1,0x43F9,0x23F9,0x03F1,0x23F1,0xA3A8,0x0981,0x86A8,0x20B8,0x4488,0x0268,0x0060,0x0060,0x0148,0x4360,0x8758,0xAA48,0xAB40,0x6650,0x4248,0x0040,0x0028,0x0028,0x0018,0x0018,0x0018,0x0010,0x4008,0x2108,0x2110,0x4110,0x4110,0x6208,0x6310,0x8408,0xA408,0xC408,0xE408,0x0409,0x2309,0x6101,0x8009,0x8009,0xC011,0x200A,0x801A,0xE022,0x0023,0x002B,0x402B,0x6033,0xC033,0x2044,0x424C,0x4354,0xE65B,0x8863,0x2A5B,0xEA5A,0xE95A,0xC95A,0xC95A,0xA952,0xA852,0x8952,0x894A,0x8852,0x8852,0x684A,0x6952,0x684A,0x484A,0x474A,}, +{0x66FA,0xA8FA,0x45FA,0x45FA,0x05FA,0x25FA,0x46FA,0x67FA,0x67FA,0x87FA,0x88FA,0xEAFA,0xCAFA,0xEBFA,0xCAFA,0x4BFB,0x6BFB,0x4AFB,0x4AFB,0x6CFB,0x2BFB,0xADFB,0x2FFC,0x4CFB,0x50FC,0xCFFB,0x2EFC,0x6DFB,0x46FA,0xC5F9,0x44F1,0xEAE1,0xC7D9,0xEBD1,0xACDA,0x2CCA,0x0CDA,0x2AE2,0x0CD2,0x8DD2,0x6CE2,0xEDC9,0xACE1,0x0DFA,0xCEFA,0x2FFB,0x8CFB,0x8DFB,0x0BFB,0x4AFB,0xE9FA,0x88FA,0xE9FA,0xC9FA,0x88FA,0x46FA,0x69FA,0x26FA,0xA5F9,0xC5F9,0x05E9,0xC5B0,0xC5D0,0x60D8,0x67A0,0x6D68,0x2188,0x0090,0x4090,0x2080,0x8378,0xA670,0x8470,0x4170,0x2070,0x2068,0x0050,0x0040,0x0028,0x0020,0x0028,0x0020,0x2018,0x4110,0x4110,0x4210,0x6210,0x6210,0x8410,0xA410,0xC408,0xE510,0x0309,0x0309,0x4109,0x8009,0xA009,0xC009,0xA011,0x000A,0x4012,0xA01A,0xC022,0xC022,0xE022,0x202B,0x803B,0x0044,0x814C,0xC354,0x845C,0x265C,0x8963,0x095B,0xC95A,0xC95A,0xA952,0xA952,0xA852,0xA852,0x894A,0x8852,0x884A,0x6852,0x684A,0x6842,0x484A,0x4842,}, +{0xE4F9,0x25FA,0x26FA,0x66FA,0x24FA,0x25FA,0x45FA,0x87FA,0x86FA,0xA7FA,0x09FB,0xEAFA,0xE8FA,0x2AFB,0x2AFB,0x4CFB,0x8CFB,0x6DFB,0xCCFB,0xEDFB,0xCDFB,0x2FFC,0x71FC,0xEEFB,0x71FC,0xEEFB,0x2EFC,0x8CFB,0xE9FA,0x47FA,0x46FA,0xEAFA,0x48FA,0x29F2,0x8DF3,0x49EA,0x2CF2,0xCDF2,0x2CEA,0x6BEA,0xEBEA,0xCEF2,0xCDFA,0xABFA,0xC9FA,0x2BFB,0x6CFB,0x8DFB,0x8CFB,0x4CFB,0x6BFB,0xEAFA,0x09FB,0x8BFB,0x0AFB,0xEAFA,0xC9FA,0xEBFA,0x68FA,0x27FA,0xE5F9,0x23E9,0x05D1,0xC2F0,0x80F0,0x6AB0,0x85A8,0x40B8,0x80D0,0x80C0,0x60A8,0x60A0,0x40A0,0x4098,0x4090,0x4090,0x2078,0x0060,0x0048,0x0038,0x0040,0x0030,0x0028,0x2020,0x4020,0x2128,0x4120,0x4120,0x6220,0x8220,0xA418,0xE410,0x0209,0x2001,0x4009,0x6011,0xA011,0xC009,0xC011,0xC009,0x000A,0x2012,0x4022,0x601A,0xA022,0xE022,0x602B,0xC03B,0x6044,0xE14C,0x025D,0xE464,0x475C,0x8963,0x095B,0xC952,0xA952,0xA952,0x8952,0x8852,0x8852,0x884A,0x684A,0x684A,0x4852,0x484A,0x484A,0x2742,}, +{0xE4F9,0x24FA,0x05FA,0x07FA,0x46FA,0x45FA,0x66FA,0x46FA,0x66FA,0x66FA,0x67FA,0xE9FA,0x29FB,0x0AFB,0x4BFB,0x8BFB,0xACFB,0x6CFB,0xACFB,0xCEFB,0xEEFB,0x2FFC,0x71FC,0x0FFC,0x70FC,0xEFFB,0x4FFC,0x8DFB,0x8BFB,0xC9FA,0x29FB,0xADFB,0xAEFB,0x2BFB,0x10FC,0x68FA,0x06FA,0xE9FA,0x89F2,0xA7FA,0x6BFB,0x6CFB,0x2BFB,0x0AFB,0xC9FA,0x2BFB,0x2AFB,0x6CFB,0xADFB,0x6BFB,0x6CFB,0x2BFB,0x6BFB,0x4AFB,0x0AFB,0x0BFB,0x4CFB,0xACFA,0x0CFB,0xEBFA,0x89FA,0x67FA,0x67E9,0x45F9,0x64F9,0x43F9,0x04D9,0xC1E0,0xE1F0,0x01E9,0xA0E0,0xA0D0,0x80C8,0x60C0,0x60B8,0x60B0,0x40A8,0x6088,0x2070,0x0058,0x0058,0x0050,0x2048,0x2038,0x2038,0x4030,0x4028,0x4030,0x2038,0x4030,0x8030,0xE118,0x0011,0x0001,0x0009,0x4009,0x8009,0xA009,0xA009,0xA011,0xA009,0xC009,0xC011,0x201A,0x601A,0xA022,0x002B,0x602B,0xE02B,0xA144,0x2155,0x6365,0xE45C,0x265C,0x885B,0xE95A,0x895A,0xA952,0xA852,0x6852,0x6852,0x6852,0x684A,0x684A,0x484A,0x484A,0x2742,0x2842,}, +{0x65FA,0x44FA,0x05FA,0xE6F9,0xC7F9,0x28FA,0xA7FA,0x86FA,0x25FA,0xE5F9,0x42F1,0x09FB,0xE9FA,0x2BFB,0x4BFB,0xCDFB,0xADFB,0xCDFB,0xEDFB,0xEEFB,0x0FFC,0x2FFC,0x30FC,0x0EFC,0x0FFC,0x0EFC,0x2EFC,0x6CFB,0x6BFB,0x6CFB,0xCCFB,0x91FC,0x51FC,0x30FC,0x0FFC,0x4CFB,0x88FA,0x2AFB,0x0AFB,0x8BFB,0x4AFB,0xEDFB,0x4BFB,0x0AFB,0xA9FA,0x4CFB,0x6CFB,0x2BFB,0xAEFB,0x4BFB,0xADFB,0x4CFB,0x8DFB,0x6CFB,0x0BFB,0xE9FA,0x6CFB,0x8EFB,0x6DFB,0x2BFB,0xEBFA,0x88FA,0x48FA,0xC6F9,0x48FA,0x89FA,0x27FA,0xE5F9,0x43F9,0xA5F9,0x01F9,0xE1F0,0xC1E8,0xA0D8,0x80D0,0x60C8,0x60C0,0x80B0,0x6090,0x4080,0x2068,0x2068,0x2068,0x2058,0x2058,0x4050,0x2048,0x2050,0x2050,0x2050,0x8040,0xA028,0xC018,0xC008,0xE008,0x0009,0x4001,0x4011,0x4009,0x8011,0x8011,0xA011,0xC011,0xE019,0x4012,0x801A,0xC02A,0xE022,0x4023,0x002C,0xE04C,0x8255,0x6365,0xC35C,0x055C,0x485B,0xC952,0xA95A,0xA952,0x8952,0x8952,0x684A,0x684A,0x684A,0x484A,0x484A,0x2742,0x2742,}, +{0x86FA,0x66FA,0x25FA,0x04FA,0x28FA,0x0AFA,0x27FA,0x48FA,0x45FA,0xE1F8,0x26FA,0xA8FA,0x26FA,0x87FA,0xE9FA,0x4BFB,0x4AFB,0x8CFB,0x6BFB,0xCEFB,0xCDFB,0xCDFB,0xEEFB,0xEDFB,0xCDFB,0xCBFB,0xECFB,0x6BFB,0xACFB,0x2FFC,0x4FFC,0x14FD,0x14FD,0xD3FC,0xB1FC,0x2FFC,0x6CFB,0x8CFB,0x2AFB,0x30FC,0x8BFB,0x0EFC,0x0AFB,0x0AFB,0x0AFB,0x4BFB,0x6DFB,0x4BFB,0xF0FB,0x4BFB,0x8DFB,0x6DFB,0x8CFB,0x8EFB,0x4DFB,0x2AFB,0xCDFB,0xAFFB,0x2BFB,0x2CFB,0xE9FA,0xEBFA,0xCAFA,0x89FA,0xC9FA,0xA8FA,0xA9FA,0x68FA,0xE5F9,0x27FA,0xA5F9,0x22F9,0x02F9,0xE1F0,0xC1E8,0x80D8,0x60D8,0xC0C8,0xA0C0,0x60A0,0x4090,0x2080,0x4070,0x6068,0x6068,0x4068,0x4068,0x4068,0x4060,0x6058,0xC030,0xC018,0xC008,0xE008,0x0001,0x0009,0x4109,0x4009,0x2009,0x4009,0x8009,0xA011,0xC011,0xE011,0x201A,0x601A,0xC022,0xC022,0xE01A,0x6023,0x603C,0x414D,0x8155,0x225D,0x6354,0xA54B,0xE852,0xA952,0xA852,0x8852,0x6952,0x684A,0x6842,0x484A,0x484A,0x284A,0x2742,0x2742,}, +{0xE7FA,0xE8FA,0xA7FA,0x67FA,0x68FA,0x6AFA,0x0AFA,0xA7F9,0xA5F9,0xE1F8,0xC9FA,0xE6F9,0x26FA,0x46FA,0x87FA,0xA8FA,0xA8FA,0xE9FA,0x2AFB,0x6CFB,0x8DFB,0x6BFB,0x8CFB,0x4BFB,0x4AFB,0x6AFB,0x8CFB,0x8DFB,0x0EFC,0x70FC,0x90FC,0x34FD,0x75FD,0x34FD,0x13FD,0x90FC,0x2EFC,0xACFB,0x29FB,0xCEFB,0x8BFB,0x8BFB,0x09FB,0xE9FA,0x8BFB,0xC9FA,0x8CFB,0x4BFB,0xEFFB,0x8CFB,0xEEFB,0xEFFB,0xCCFB,0xCFFB,0x6EFB,0x8AFB,0xECFB,0xACFB,0x6DFB,0x8EFB,0x49FB,0x6DFB,0x2AFB,0xEAFA,0xEAFA,0xC8FA,0xCAFA,0x88FA,0x26FA,0x06FA,0x07FA,0xA5F9,0x84F9,0x43F9,0x02F9,0xE1F0,0xE0E8,0xE0E0,0xE0D8,0xC0C8,0x80B8,0x60A0,0x6080,0x6060,0xA050,0x8058,0x8070,0x8078,0x6068,0xA040,0xE010,0xE000,0xC008,0xE000,0x0009,0x4219,0x4321,0x4011,0x2009,0x2009,0x6011,0xA011,0xE019,0x2012,0x201A,0x601A,0xA022,0xC02A,0xC01A,0x2023,0x0034,0xE034,0x014D,0x014D,0xA154,0x0344,0x454B,0xE852,0xA952,0x8852,0x694A,0x684A,0x484A,0x484A,0x284A,0x2842,0x0742,0x2742,}, +{0xE8FA,0x09FB,0xA8FA,0xA8FA,0x88FA,0x68FA,0x48FA,0xE8F9,0xA5F9,0x43F9,0xC8F1,0xE5F9,0x06FA,0x67FA,0x46FA,0x26FA,0x46FA,0x67FA,0x66FA,0xC9FA,0x2BFB,0xC9FA,0x87FA,0x87FA,0xE8FA,0x2AFB,0x8DFB,0x8DFB,0x2FFC,0x6FFC,0x90FC,0x34FD,0x54FD,0x74FD,0x32FD,0xD1FC,0x4EFC,0x6AFB,0x4AFB,0xACFB,0xACFB,0xACFB,0x4AFB,0xE8FA,0x8BFB,0xA8FA,0x8CFB,0xE9FA,0xCEFB,0x8CFB,0x0EFC,0xCEFB,0xCCFB,0xCFFB,0x8FFB,0xEBFB,0x2BFC,0x0BFC,0xADFB,0xCFFB,0xC9FB,0x8BFB,0x4AFB,0x0BFB,0xE9FA,0xC8FA,0xCAFA,0xC8FA,0x26FA,0x06FA,0x27FA,0xE6F9,0xA5F9,0x84F9,0x63F9,0x22F9,0x02F9,0x01F1,0x00E9,0x41E1,0x02D1,0xA0B8,0xA098,0xA048,0xE020,0xE020,0xC038,0xA058,0xA050,0xE018,0xE008,0xE000,0xE000,0x0001,0x4109,0xA421,0xC529,0xA211,0x4009,0x4009,0x6009,0xA011,0xE011,0x4022,0x6022,0xA01A,0xE022,0x0023,0x2023,0x602B,0x002C,0x6034,0xA034,0xA03C,0x6044,0x4144,0xC343,0x4553,0xE852,0xA952,0x884A,0x484A,0x4842,0x284A,0x2842,0x2742,0x2742,0x063A,}, +{0xC8FA,0xC8FA,0x87FA,0x87FA,0x88FA,0x88FA,0x68FA,0x27FA,0x05FA,0xA9F9,0x86F9,0x46F9,0xC6F9,0x27FA,0x25FA,0xE5F9,0x26FA,0x46FA,0x46FA,0x66FA,0x88FA,0xCAFA,0x26FA,0xA7FA,0x4AFB,0x2BFB,0x6CFB,0x8CFB,0x2FFC,0x0EFC,0x6FFC,0xD1FC,0xD2FC,0xF1FC,0xD0FC,0xB0FC,0x2EFC,0x4AFB,0x8BFB,0x4AFB,0x0EFC,0xCDFB,0xCCFB,0x6BFB,0xACFB,0x0AFB,0x6BFB,0xE9FA,0x2BFB,0x6CFB,0x8CFB,0xF0FB,0xCCFB,0x8DFB,0x70EB,0xEBFB,0x2BFC,0x2BFC,0xEBFB,0xAFFB,0xECFB,0x89FB,0x69FB,0x08FB,0x09FB,0xC9FA,0xA9FA,0xA8FA,0x26FA,0x07FA,0x48FA,0x27FA,0xE5F9,0xA4F9,0x43F9,0x22F9,0x01F9,0xE1F0,0xC0F0,0x20E1,0x01E1,0xC1D0,0xA098,0xC138,0x0111,0x0009,0x0009,0x0019,0xE018,0xE008,0xE000,0x0001,0x2109,0x6109,0x8421,0x062A,0x273A,0x262A,0xE319,0xA211,0x8111,0xC119,0x2122,0x8122,0xA022,0xE022,0x022B,0x412B,0x812B,0xE033,0x403C,0x803C,0x803C,0x403C,0x203C,0x203C,0x0144,0xA443,0x264B,0x074B,0x884A,0x684A,0x484A,0x2742,0x2742,0x2742,0x0742,0x063A,}, +{0x87FA,0x67FA,0x87FA,0x87FA,0x87FA,0x48FA,0x47FA,0x26FA,0xE6F9,0xE6F9,0xC5F9,0x85F9,0x44F9,0xC4F8,0xA6F9,0xA5F9,0x26FA,0x86FA,0x26FA,0x25FA,0x87FA,0x68FA,0x87FA,0xE9FA,0x0AFB,0x4AFB,0x2AFB,0xCDFB,0x0EFC,0x0EFC,0x2FFC,0x4FFC,0x6FFC,0x6FFC,0x4EFC,0x2EFC,0xEDFB,0x6BFB,0x8BFB,0x4BFB,0x4FFC,0x0DFC,0x0EFC,0xECFB,0x8CFB,0x6BFB,0xADFB,0x4BFB,0x0AFB,0xC9FA,0x2AFB,0xAEFB,0xCDFB,0xADFB,0x6EFB,0xCDFB,0x0BFC,0xEBFB,0x0AFC,0xCBFB,0xA9FB,0xA8FB,0x68FB,0x29FB,0x09FB,0xC9FA,0xA8FA,0x68FA,0x26FA,0x47FA,0xA8FA,0x26FA,0x26FA,0xE5F9,0x63F9,0x22F9,0xE1F8,0xC1F8,0xC0F8,0xE0F0,0xE0E0,0xE0C8,0xA280,0xE240,0x0011,0xE008,0x0009,0x2109,0x4111,0x2009,0x2001,0x4311,0xA521,0xE629,0x483A,0x8942,0x8A4A,0xAA42,0x683A,0x2622,0xE429,0x0322,0x2222,0x6122,0xA222,0x232B,0x873B,0xE63B,0x043C,0x4134,0x8044,0xC044,0xC04C,0x8044,0x403C,0x6034,0x4044,0x0144,0xA34B,0x464B,0xE74A,0x684A,0x4842,0x2742,0x2842,0x0742,0x073A,0xE739,}, +{0xA4F9,0xE4F9,0x26FA,0x26FA,0x46FA,0x27FA,0x26FA,0x25FA,0x05FA,0xC5F9,0xC5F9,0x27FA,0xE2F8,0x02F9,0x43F9,0x24F9,0xA5F9,0x06FA,0xE5F9,0x47FA,0x67FA,0x67FA,0xE9FA,0xA8FA,0x4AFB,0x49FB,0x4AFB,0xACFB,0xCDFB,0xCCFB,0xEDFB,0x0DFC,0x0DFC,0x0DFC,0xACFB,0x8CFB,0x6BFB,0x8BFB,0x8CFB,0x8CFB,0x2EFC,0xEDFB,0x0DFC,0x0EFC,0xEDFB,0x4AFB,0xADFB,0xEFFB,0x2BFB,0x0AFB,0x8EFB,0x6EFB,0x8DFB,0xD0FB,0x2BFB,0xAEFB,0xEDFB,0xABFB,0xABFB,0xAAFB,0x89FB,0x89FB,0x69FB,0x09FB,0xC8FA,0x88FA,0x67FA,0x47FA,0x48FA,0xA9FA,0x09FB,0x87FA,0x46FA,0xE4F9,0xA4F9,0xA4F9,0x22F9,0x22F9,0xE2F8,0x02F9,0x00E9,0xE0B8,0x0479,0x2471,0x2131,0x0111,0x6321,0xA531,0xC631,0xA421,0x8429,0x0842,0xAB52,0xEC62,0xEC62,0xEB5A,0xEB52,0x0C5B,0x0C5B,0xEB42,0xA83A,0x6632,0x431A,0x631A,0x0833,0x0D54,0x8F6C,0x8E64,0x6844,0x4334,0x813C,0xA04C,0xC04C,0xA04C,0xA044,0xA044,0xA044,0xA044,0x224C,0xC34B,0x4553,0xC74A,0x4742,0x2742,0x0742,0x0742,0xE639,0xE639,}, +{0x42F9,0x83F9,0xC4F9,0xE5F9,0xE4F9,0x26FA,0x05FA,0x25FA,0xE4F9,0xA4F9,0x83F9,0x44F9,0xE1F8,0x22F9,0x22F9,0x01F9,0x42F9,0x63F9,0xA3F9,0x25FA,0x05FA,0x87FA,0xE9FA,0x26FA,0x49FB,0x8BFB,0x8BFB,0x4AFB,0x4BFB,0x29FB,0x29FB,0xABFB,0x8BFB,0x6AFB,0x49FB,0x09FB,0x0AFB,0x4AFB,0xABFB,0xACFB,0xECFB,0x0DFC,0x0DFC,0x2DFC,0x0DFC,0x8BFB,0x49FB,0x0FFC,0x0AFB,0x2AFB,0xF0FB,0x4CFB,0x6CFB,0x6EFB,0x0BFB,0x8EFB,0x8CFB,0x6BFB,0x4AFB,0x29FB,0x08FB,0x08FB,0xE8FA,0xC8FA,0x67FA,0x26FA,0x26FA,0x47FA,0x88FA,0xEAFA,0x6AFB,0x08FB,0x86FA,0x84FA,0x44FA,0xE5F9,0xC4F9,0xA4F9,0xA4F9,0x43E9,0x23C1,0x23B1,0x44A9,0x84B1,0xA491,0xC551,0xE749,0x0742,0x0742,0x073A,0x2842,0x8A52,0xEB5A,0xEC62,0xCB5A,0xEB5A,0x2D6B,0x6F73,0xB06B,0xD273,0xEF6B,0xCD5B,0x6C4B,0x6D43,0x516C,0x158D,0x5695,0x537D,0xEF64,0x894C,0x233C,0x603C,0x8044,0xC044,0xC044,0xE04C,0x004D,0x0055,0xC04C,0x414C,0xA34B,0x464B,0xA74A,0x2742,0x0742,0x073A,0xE639,0xE639,}, +{0x42F9,0x42F9,0x83F9,0x42F9,0x82F9,0xC4F9,0xC3F9,0xC3F9,0xA3F9,0x83F9,0x22F9,0xE1F8,0xA0F8,0xA2F8,0x01F9,0xA4F9,0x62F9,0x83F9,0xA4F9,0xA4F9,0xC4F9,0x25FA,0x46FA,0x42F9,0xA8FA,0x6CFB,0x2AFB,0x09FB,0x66FA,0xE8FA,0xE8FA,0x4AFB,0xE9FA,0xE9FA,0x2AFB,0x09FB,0x2AFB,0x8CFB,0x6AFB,0x8AFB,0xABFB,0xECFB,0x0CFC,0x2DFC,0x0CFC,0xCCFB,0x8AFB,0x4EFC,0xACFB,0x8BFB,0x4CFB,0x2BFB,0x8DFB,0x0BFB,0x0BFB,0x6CFB,0x4AFB,0xE9FA,0xE8FA,0xA8FA,0x87FA,0xC7FA,0x87FA,0x87FA,0x46FA,0x26FA,0x67FA,0x87FA,0xA7FA,0x89FB,0xA9FB,0x69FB,0x87FB,0x87FB,0x46FB,0x05FB,0x44FA,0xC4F1,0xC5E9,0x25D1,0x25B1,0x65B1,0xA5D9,0xC4D9,0x26DA,0x889A,0x4852,0x074A,0x2842,0x2842,0x4742,0x684A,0x684A,0x4842,0x484A,0x894A,0xCA52,0x0C5B,0x6F73,0xD17B,0x7384,0xF79C,0x3695,0x378D,0x789D,0xB99D,0xB89D,0xD8A5,0xD7A5,0x937D,0xCB54,0x443C,0x4034,0x4034,0x6044,0xA04C,0xC04C,0x004D,0xE04C,0x804C,0x024C,0x834B,0x064B,0x6742,0x0742,0x063A,0xE639,0xE639,}, +{0x41F9,0x21F9,0x22F9,0x41F9,0x83F9,0x83F9,0x83F9,0x63F9,0x62F9,0x22F9,0x02F9,0x02F9,0xC0F8,0xA0F8,0x02F9,0x63F9,0xE5F9,0xC3F9,0xC3F9,0x04FA,0xE3F9,0x04FA,0xE4F9,0x42F9,0x86FA,0x0AFB,0xA8FA,0x87FA,0x45FA,0x87FA,0xA7FA,0xE8FA,0xC7FA,0x08FB,0x29FB,0x4BFB,0x8BFB,0x8BFB,0x49FB,0x48FB,0x49FB,0x8AFB,0xABFB,0x0CFC,0x0CFC,0xCBFB,0xCBFB,0x0BFC,0xEBFB,0xEAFB,0x0DFC,0x2FFC,0xEFFB,0x4AFB,0x6BFB,0x6CFB,0x28FB,0xC8FA,0xE8FA,0x67FA,0x87FA,0x86FA,0xA7FA,0xA7FA,0x67FA,0x67FA,0xE8FA,0x49FB,0xC8FB,0xA9FC,0x86FC,0x69FC,0xA8FC,0x88FC,0x07FC,0x67FB,0x64FA,0xA3E9,0x64E1,0x03D9,0x04D9,0x63D1,0xA4F1,0xC4F9,0xE5F1,0x47CA,0x2862,0x0842,0x0742,0x0742,0x063A,0x2642,0x2642,0x2642,0x4642,0x684A,0x894A,0xA952,0xAA52,0xCB52,0x8F6B,0xD694,0x9BAD,0x1CB6,0x5CB6,0x3AA6,0x5BAE,0x5CBE,0x7BBE,0x18AE,0x7385,0xAB5C,0x023C,0xC033,0xC03B,0x203C,0x8044,0xC044,0xC04C,0xA044,0x404C,0xA24B,0x444B,0xC64A,0x2742,0xE639,0xC639,0xC639,}, +{0x21F9,0x22F9,0x42F9,0xA3F9,0x62F9,0x83F9,0x62F9,0x42F9,0x22F9,0x43F9,0x01F9,0x83F9,0x01F9,0x22F9,0x62F9,0x63F9,0xA4F9,0xE5F9,0x05FA,0x45FA,0x24FA,0x86FA,0xA4F9,0x63F9,0x28FB,0xEAFA,0xA7FA,0x25FA,0x46FA,0x87FA,0xC7FA,0x08FB,0x49FB,0x29FB,0x6AFB,0x6BFB,0xABFB,0x4AFB,0x49FB,0x49FB,0x29FB,0x28FB,0x69FB,0xAAFB,0xAAFB,0xCBFB,0xCBFB,0x8AFB,0xAAFB,0xCAFB,0xEBFB,0x0CFC,0xEEFB,0xCAFB,0xAAFB,0x4BFB,0x2BFB,0x09FB,0xE9FA,0x66FA,0xA8FA,0xA7FA,0x88FA,0xC7FA,0x67FA,0xC7FA,0x89FB,0x2BFC,0x88FC,0x67FD,0x87FD,0x69FD,0x28FD,0xC7FC,0xE6FB,0xE5FA,0x45FA,0xE4F9,0x21F9,0x02F1,0x22F9,0x82F1,0xA3F9,0xA4F9,0xC5F9,0xC5E1,0xC689,0xC641,0xC739,0x073A,0x063A,0x263A,0x253A,0x263A,0x453A,0x863A,0x853A,0x4532,0x0532,0x0632,0x6842,0x4C53,0x737C,0x79A5,0x1CBE,0x7EBE,0x9DBE,0x9CB6,0x3AAE,0xD79D,0x7495,0xCD64,0xE333,0x602B,0x802B,0xA033,0x4034,0x6044,0x8044,0x603C,0x203C,0xC043,0x414B,0xC442,0x4642,0xE639,0xC639,0xC639,}, +{0x01F1,0x63F9,0x62F9,0xA3F9,0x83F9,0x63F9,0x63F9,0x42F9,0x63F9,0x22F9,0x43F9,0xA4F9,0x83F9,0xA3F9,0xE4F9,0x24FA,0x25FA,0xE4F9,0xE6F9,0x26FA,0x46FA,0x87FA,0xA4F9,0xA4F9,0x6AFB,0xA8FA,0x88FA,0x25FA,0xA6FA,0xE8FA,0x49FB,0x49FB,0x8AFB,0x8AFB,0xEBFB,0xCBFB,0x69FB,0x6AFB,0x49FB,0x28FB,0x28FB,0x28FB,0x08FB,0x48FB,0x49FB,0x49FB,0x49FB,0x69FB,0x6AFB,0x89FB,0x88FB,0x89FB,0xACFB,0x8AFB,0x69FB,0x4CFB,0xECFA,0xAAFA,0x8BFA,0x26FA,0x49FA,0x29FA,0xC7F9,0x08FA,0x48FA,0xC7FA,0x27FB,0x49FC,0xA7FC,0x86FD,0x86FD,0x47FD,0xA7FC,0x46FC,0x25FB,0x45FA,0xE4F9,0x62F9,0x21F9,0x63F9,0x62F9,0xA2F9,0xA3F9,0x83F9,0x84F9,0x85F9,0x43C9,0x4479,0xA651,0xE741,0x273A,0x483A,0x473A,0x473A,0x6532,0x8432,0x6432,0x632A,0x242A,0x2222,0x2322,0x652A,0x0843,0xEF63,0xF68C,0xDBB5,0x3DBE,0x3BB6,0xD895,0x768D,0x358D,0xAF6C,0xE63B,0x8023,0x8023,0xA033,0xE033,0x203C,0x203C,0x003C,0xA03B,0x8033,0x203B,0xC242,0x6442,0x263A,0xC639,0xA639,}, +{0xA0F0,0xE0F8,0x41F9,0x62F9,0x83F9,0x83F9,0x63F9,0x42F9,0x62F9,0x42F9,0x22F9,0xC5F9,0xA4F9,0xE4F9,0xE4F9,0x65FA,0x87FA,0x05FA,0x05FA,0x26FA,0xE7F9,0xE6F9,0xC5F9,0x05FA,0xCAFA,0x26FA,0x47FA,0x87FA,0xC7FA,0x08FB,0x4AFB,0xABFB,0xEBFB,0xECFB,0xECFB,0x0CFC,0xEBFB,0xCBFB,0x89FB,0x48FB,0x68FB,0x27FB,0x07FB,0x68FB,0x28FB,0xE7FA,0x07FB,0x07FB,0x28FB,0x06FB,0x67FB,0x28FB,0xABFB,0x09FB,0x08FB,0xC8FA,0x68FA,0x89FA,0x48FA,0xE5F9,0x48FA,0x49FA,0x29FA,0x08FA,0x07FA,0x67FA,0x86FA,0x46FB,0xC7FB,0x46FC,0x65FC,0x05FC,0x87FB,0x46FB,0x64FA,0x04FA,0xC4F9,0x83F9,0x82F9,0x22F9,0x42E9,0x82F1,0x62F9,0x21F9,0x83F9,0xA5F9,0x23F1,0x01B9,0x2391,0x8671,0x2842,0x8842,0x6842,0x663A,0x853A,0x853A,0xA332,0x822A,0x622A,0x621A,0x4422,0x632A,0x842A,0xA632,0x2B43,0xEF5B,0x9374,0xF694,0x379D,0x589D,0x5695,0xF27C,0x6D5C,0xE53B,0xE33B,0xE13B,0xC03B,0xC03B,0xE033,0xA033,0x4033,0x0033,0x002B,0xA13A,0x833A,0x453A,0xE631,0xA531,}, +{0xC0F8,0xA0F8,0xE1F8,0xC0F8,0xE1F8,0x01F9,0x01F9,0x02F9,0x02F9,0x43F9,0x22F9,0x06FA,0xE4F9,0xE4F9,0xE4F9,0x25FA,0x06FA,0x25FA,0x05FA,0x05FA,0x05FA,0x26FA,0x05FA,0x47FA,0x07FA,0x06FA,0xE7F9,0x48FA,0x88FA,0xE8FA,0x4AFB,0xABFB,0xCCFB,0xEBFB,0x0CFC,0x0CFC,0x2BFC,0x0BFC,0xA9FB,0xC9FB,0xC8FB,0xA7FB,0x88FB,0xC9FB,0x27FB,0x26FB,0x46FB,0x27FB,0x07FB,0x06FB,0x07FB,0x06FB,0x28FB,0xE6FA,0xA6FA,0x65FA,0x67FA,0x67FA,0x05FA,0x26FA,0x46FA,0x87FA,0x25FA,0x06FA,0x06FA,0x67FA,0xA6FA,0x86FA,0xE7FA,0xE6FA,0x06FB,0xC5FA,0xE6FA,0xA6FA,0x44FA,0xE3F9,0xC3F9,0xC4F9,0x43F9,0x23F9,0x22F1,0x42F9,0x41F9,0x42F9,0x62F9,0x64F9,0x22F9,0x21D9,0x21A1,0x4491,0xC641,0x273A,0x673A,0x873A,0x863A,0x8532,0x8432,0x6332,0x4432,0x4532,0x4532,0x673A,0x8842,0x6842,0x4742,0x873A,0xC942,0x4C4B,0xCF63,0x737C,0xB594,0xD48C,0x7174,0x0C5C,0xEB6B,0x0544,0xA03B,0xC03B,0x8033,0x6033,0x2033,0xC032,0xA02A,0xA032,0x823A,0x633A,0x453A,0xC531,}, +{0xC0F8,0xE1F8,0xE2F8,0xC0F8,0xE1F8,0x22F9,0xE1F8,0x02F9,0x43F9,0x43F9,0xA3F9,0x25FA,0x05FA,0x05FA,0x25FA,0x66FA,0x45FA,0x26FA,0x26FA,0x67FA,0x47FA,0x67FA,0x47FA,0xA8FA,0x87FA,0x05FA,0x06FA,0xA7FA,0xA7FA,0xA5FA,0x08FB,0x69FB,0xAAFB,0xCBFB,0x0BFC,0x0BFC,0x0AFC,0x4AFC,0x4AFC,0x69FC,0xA9FC,0xC8FC,0x28FC,0xA9FC,0xE8FB,0x27FC,0x27FC,0xA6FB,0x45FB,0x46FB,0xE5FA,0x06FB,0x07FB,0xC5FA,0x85FA,0x85FA,0x65FA,0x65FA,0x65FA,0xC6FA,0xC5FA,0xA5FA,0x85FA,0x66FA,0x67FA,0x26FA,0x46FA,0x87FA,0x87FA,0xA6FA,0xA6FA,0x86FA,0xA6FA,0xC7FA,0x65FA,0x04FA,0xA3F9,0x24F9,0x23F1,0x43F9,0xA3F9,0x63F9,0x41F9,0x42F9,0x62F9,0x63F9,0x21F9,0x41F9,0x41C1,0x2489,0xA559,0x063A,0x473A,0x673A,0x663A,0x6632,0x653A,0x443A,0x052A,0x262A,0x473A,0x694A,0x894A,0x6842,0x463A,0x4532,0x2532,0x4432,0xA73A,0x2A3B,0xCE5B,0x116C,0xCF63,0xAC5B,0xEB5B,0xE53B,0xC23B,0xE03B,0xC03B,0xA03B,0x6033,0xE032,0xA02A,0x802A,0xA13A,0xA132,0x833A,0x443A,}, +{0x80F0,0xE2F8,0xA0F8,0x02F9,0x01F9,0x43F9,0xE1F0,0x43F9,0x63F9,0xA4F9,0x64FA,0x86FA,0x85FA,0x86FA,0x66FA,0xC7FA,0x87FA,0xA8FA,0xA7FA,0xE9FA,0xE9FA,0x89FA,0x68FA,0x88FA,0xC8FA,0xA7FA,0x87FA,0xC7FA,0xC7FA,0xC6FA,0xE6FA,0x06FB,0x48FB,0x68FB,0xA8FB,0xE8FB,0xC8FB,0x0AFC,0x69FC,0xCAFC,0x29FD,0x4AFD,0xE8FC,0x2BFD,0x88FC,0xA8FC,0x67FC,0x27FC,0xC7FB,0x87FB,0x06FB,0xE5FA,0x06FB,0x84FA,0x85FA,0xA5FA,0xA5FA,0xA5FA,0xE7FA,0x07FB,0x06FB,0x06FB,0x65FA,0x85FA,0x24FA,0x86FA,0x66FA,0xE8FA,0xC6FA,0xE7FA,0xE6FA,0xA6FA,0xA5FA,0xC7FA,0x66FA,0xA4F9,0x24F9,0x03F1,0xA3F9,0xC3F9,0xE5F9,0xE5F9,0x82F9,0x62F9,0x62F9,0x22F9,0xE0F8,0x60F9,0xC1E9,0x00C1,0x2191,0xE549,0x6732,0x683A,0x473A,0x262A,0x4632,0x463A,0x473A,0x473A,0x483A,0x2842,0x4842,0x4842,0x473A,0x2532,0x042A,0xE429,0x0432,0x6632,0xEA4A,0x4E5B,0x6E63,0x6D5B,0xAD6B,0xCB5B,0xA74B,0xC44B,0xC353,0xC34B,0x8143,0x002B,0x8022,0x8022,0xC02A,0x0133,0xC23A,0x633A,}, +{0x41F8,0x62F0,0x60F0,0xC2F8,0xE1F8,0x43F9,0x01F9,0x41F9,0x62F1,0x45FA,0xA6FA,0x04F2,0xA5FA,0x06FA,0xC9FA,0xA8FA,0xAAFA,0xABFA,0x0CFB,0x0BFB,0x0CFB,0x69FA,0x87FA,0xE8FA,0x89FB,0x07FB,0xE7FA,0xC7FA,0x07FB,0xE6FA,0xE6FA,0xC5FA,0xE6FA,0x05FB,0x46FB,0x67FB,0x67FB,0x67FB,0xE7FB,0x28FC,0x67FC,0x25FC,0x86FC,0x89FC,0x68FC,0x28FC,0xE7FB,0xC8FB,0x68FB,0x47FB,0x27FB,0x05FB,0xE6FA,0xE4FA,0x06FB,0xC5FA,0x06FB,0xC5FA,0xC5FA,0xE8FA,0x85FA,0x44FA,0x04FA,0x65FA,0x24FA,0x86FA,0xC5FA,0x47FB,0x86FB,0x87FB,0x67FB,0xE5FA,0xC6FA,0xC6FA,0x64FA,0xA3F9,0x82F9,0x84F9,0xE3F9,0x44FA,0x45FA,0x66FA,0x62F9,0x20F9,0x41F9,0x00F9,0xE0F8,0x60F9,0xA0F1,0xE0D0,0x01B1,0xE661,0x483A,0xA94A,0xE94A,0xA842,0xC84A,0xA74A,0x8642,0x663A,0x663A,0x463A,0x473A,0x4842,0x4832,0x463A,0x0432,0xE429,0xE329,0x0422,0x452A,0x6832,0x6A42,0x8B4A,0xEB52,0x0A4B,0x0643,0x043B,0x623B,0x613B,0x2033,0xC02A,0x8022,0xA022,0x0033,0x403B,0x423B,0x023B,}, +{0x03F9,0x81F8,0x20E8,0xA0F8,0xC0F8,0xC1F0,0x42F9,0x82F9,0xC3F9,0x45FA,0x63F1,0xA4F9,0x27FA,0x48FA,0x0BFB,0xC9FA,0xCBFA,0x0CFB,0xEBFA,0xA9FA,0x87FA,0xA7FA,0xE8FA,0x8AFB,0x0AFC,0xA8FB,0x67FB,0x48FB,0x48FB,0x27FB,0x27FB,0xC5FA,0xA4FA,0xC6FA,0x07FB,0x28FB,0xE6FA,0x26FB,0x66FB,0xA7FB,0x85FB,0xC5FB,0x05FC,0x25FC,0xC5FB,0x05FC,0x85FB,0x26FB,0x06FB,0x26FB,0x27FB,0x24FB,0x27FB,0x83FB,0x46FB,0xC5FA,0xA4FA,0xC5FA,0x84FA,0x63FA,0x62FA,0x83FA,0x63FA,0x23FA,0x63FA,0xC2FA,0xA3FB,0xA4FB,0x23FC,0x44FC,0x04FC,0x63FB,0x03FB,0xC3FA,0x43FA,0x44FA,0x23FA,0x04FA,0x43FA,0xC4FA,0xA5FA,0xC4FA,0x22FA,0x20E9,0x40F9,0xE0F8,0xC0F8,0x80F1,0xA0E9,0xC0D8,0xE1C0,0x059A,0x885A,0xEA4A,0x2A53,0x094B,0xC73A,0xA53A,0x843A,0x8432,0x8332,0x442A,0x4632,0x673A,0x473A,0x273A,0x2532,0x042A,0xE321,0xC321,0xC221,0xC421,0xC621,0xC521,0xA529,0xE429,0x042A,0x632A,0xE232,0x2033,0xE02A,0x802A,0x8022,0xA022,0x002B,0x603B,0x8143,0x6243,}, +{0x84F9,0x02F9,0x60E8,0x01F9,0x02F9,0x01F9,0x62F9,0xC3F9,0x03FA,0xE4F9,0x42F9,0x24FA,0x66FA,0x87FA,0xA7FA,0xC8FA,0xA8FA,0xA8FA,0x66FA,0x87FA,0x86FA,0xE8FA,0x69FB,0x09FC,0x89FC,0xA9FC,0xE8FB,0x09FC,0xA9FB,0x87FB,0x87FB,0x25FB,0xE5FA,0xC6FA,0x07FB,0xC7FA,0x27FB,0x07FB,0x67FB,0x46FB,0x66FB,0x85FB,0xC5FB,0xE5FB,0x26FC,0x84FC,0x05FC,0x65FB,0x85FB,0x04FC,0xE4FB,0xC4FB,0xC4FB,0x03FC,0x44FB,0xE4FA,0xC4FA,0x83FA,0x82FA,0xC2FA,0x02FB,0x62FB,0x02FB,0x03FA,0x64FB,0x23FC,0xA3FC,0xE4FC,0x24FD,0x05FD,0x43FC,0x62FB,0xE2FA,0x42FA,0x42FA,0x82FA,0xA2FA,0xA4FA,0x25FB,0x65FB,0xE4FA,0xE3FA,0xC3FA,0x81E9,0x00F1,0xA0F8,0x20E9,0xA0C9,0x40E9,0xC0E8,0xC0C8,0xE591,0xE952,0xEA4A,0x2A53,0x094B,0xC63A,0xA532,0x8332,0x6332,0x612A,0x6222,0x6322,0xA322,0x842A,0x663A,0x4532,0x2422,0xE319,0xC319,0xA221,0xA221,0x8321,0x6319,0x6319,0x6319,0xA321,0xE229,0x822A,0x0133,0xE13A,0xA12A,0xA122,0xC022,0x202B,0x6133,0x8143,0xA143,}, +}; diff --git a/include/keepkey/firmware/binance.h b/include/keepkey/firmware/binance.h index dffcad6e6..fff9c4836 100644 --- a/include/keepkey/firmware/binance.h +++ b/include/keepkey/firmware/binance.h @@ -2,7 +2,7 @@ #define KEEPKEY_FIRMWARE_BINANCE_H #include "messages.pb.h" -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include #include diff --git a/include/keepkey/firmware/coins.def b/include/keepkey/firmware/coins.def index de2643175..3d7a3a972 100644 --- a/include/keepkey/firmware/coins.def +++ b/include/keepkey/firmware/coins.def @@ -2,6 +2,7 @@ //coin_name coin_shortcut address_type maxfee_kb p2sh signed_message_header bip44_account_path forkid/chain_id decimals contract_address xpub_magic segwit force_bip143 curve_name cashaddr_prefix bech32_prefix decred xpub_magic_segwit_p2sh xpub_mmagic_segwit_native nanoaddr_prefix taproot X(true, "Bitcoin", true, "BTC", true, 0, true, 100000, true, 5, true, "Bitcoin Signed Message:\n", true, 0x80000000, false, 0, true, 8, false, NO_CONTRACT, true, 76067358, true, true, true, false, true, SECP256K1_STRING, false, "", true, "bc", false, false, true, 77429938, true, 78792518, false, "", true, true ) X(true, "Testnet", true, "TEST", true, 111, true, 10000000, true, 196, true, "Bitcoin Signed Message:\n", true, 0x80000001, false, 0, true, 8, false, NO_CONTRACT, true, 70617039, true, true, true, false, true, SECP256K1_STRING, false, "", true, "tb", false, false, true, 71979618, true, 73342198, false, "", true, true ) +#ifndef BITCOIN_ONLY X(true, "BitcoinCash", true, "BCH", true, 0, true, 500000, true, 5, true, "Bitcoin Signed Message:\n", true, 0x80000091, true, 0, true, 8, false, NO_CONTRACT, true, 76067358, true, false, true, true, true, SECP256K1_STRING, true, "bitcoincash", false, "", false, false, false, 0, false, 0, false, "", true, false ) X(true, "Namecoin", true, "NMC", true, 52, true, 10000000, true, 5, true, "Namecoin Signed Message:\n", true, 0x80000007, false, 0, true, 8, false, NO_CONTRACT, true, 27108450, true, false, true, false, true, SECP256K1_STRING, false, "", false, "", false, false, false, 0, false, 0, false, "", true, false ) X(true, "Litecoin", true, "LTC", true, 48, true, 1000000, true, 50, true, "Litecoin Signed Message:\n", true, 0x80000002, false, 0, true, 8, false, NO_CONTRACT, true, 27108450, true, true, true, false, true, SECP256K1_STRING, false, "", true, "ltc", false, false, true, 28471030, true, 78792518, false, "", true, false ) @@ -46,6 +47,6 @@ X(true, "Terra", true, "LUNA", false, NA, false, NA, false, N X(true, "Kava", true, "KAVA", false, NA, false, NA, false, NA, false, {0}, true, 0x800001cb, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "kava", false, false, false, 0, false, 0, false, "", true, false ) X(true, "Secret", true, "SCRT", false, NA, false, NA, false, NA, false, {0}, true, 0x80000211, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "secret", false, false, false, 0, false, 0, false, "", true, false ) X(true, "MAYAChain", true, "CACAO", false, NA, false, NA, false, NA, false, {0}, true, 0x800003a3, false, 0, true, 10, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "maya", false, false, false, 0, false, 0, false, "", true, false ) - +#endif // BITCOIN_ONLY #undef X #undef NO_CONTRACT diff --git a/include/keepkey/firmware/coins.h b/include/keepkey/firmware/coins.h index b94e36cca..5c6c64d1e 100644 --- a/include/keepkey/firmware/coins.h +++ b/include/keepkey/firmware/coins.h @@ -44,14 +44,20 @@ enum { CONCAT(CoinIndex, __COUNTER__), #include "keepkey/firmware/coins.def" +#ifdef BITCOIN_ONLY +// For full-featured keepkey, this is defined in ethereum_tokens.h. For bitcoin only keepkey, need to +// define it here because ethereum_tokens.h is not included in any file +#define TOKENS_COUNT 0 +#else #define X(INDEX, NAME, SYMBOL, DECIMALS, CONTRACT_ADDRESS) \ CONCAT(CoinIndex, __COUNTER__), #include "keepkey/firmware/tokens.def" - +#endif CoinIndexLast, CoinIndexFirst = 0 }; + #define COINS_COUNT ((int)CoinIndexLast - (int)CoinIndexFirst) #define NODE_STRING_LENGTH 50 diff --git a/include/keepkey/firmware/cosmos.h b/include/keepkey/firmware/cosmos.h index 439eae5e0..647c11162 100644 --- a/include/keepkey/firmware/cosmos.h +++ b/include/keepkey/firmware/cosmos.h @@ -2,7 +2,7 @@ #define KEEPKEY_FIRMWARE_COSMOS_H #include "messages.pb.h" -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include #include diff --git a/include/keepkey/firmware/crypto.h b/include/keepkey/firmware/crypto.h index 58a2a5bc6..89608011c 100644 --- a/include/keepkey/firmware/crypto.h +++ b/include/keepkey/firmware/crypto.h @@ -20,9 +20,9 @@ #ifndef CRYPTO_H #define CRYPTO_H -#include "trezor/crypto/bip32.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/bip32.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/sha2.h" #include "pb.h" #include "keepkey/transport/interface.h" diff --git a/include/keepkey/firmware/eos.h b/include/keepkey/firmware/eos.h index b905fb162..a8d71e56e 100644 --- a/include/keepkey/firmware/eos.h +++ b/include/keepkey/firmware/eos.h @@ -20,7 +20,7 @@ #ifndef KEEPKEY_FIRMWARE_EOS_H #define KEEPKEY_FIRMWARE_EOS_H -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include "messages-eos.pb.h" diff --git a/include/keepkey/firmware/ethereum.h b/include/keepkey/firmware/ethereum.h index 289d10da6..878bd5326 100644 --- a/include/keepkey/firmware/ethereum.h +++ b/include/keepkey/firmware/ethereum.h @@ -22,7 +22,7 @@ #include #include -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include "messages-ethereum.pb.h" typedef struct _EthereumSignTx EthereumSignTx; diff --git a/include/keepkey/firmware/ethereum_contracts.h b/include/keepkey/firmware/ethereum_contracts.h index 78fe1c6c9..2ea176ac3 100644 --- a/include/keepkey/firmware/ethereum_contracts.h +++ b/include/keepkey/firmware/ethereum_contracts.h @@ -21,7 +21,7 @@ #ifndef KEEPKEY_FIRMWARE_ETHEREUMCONTRACTS_H #define KEEPKEY_FIRMWARE_ETHEREUMCONTRACTS_H -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" typedef struct _EthereumSignTx EthereumSignTx; diff --git a/include/keepkey/firmware/fsm.h b/include/keepkey/firmware/fsm.h index 19e911106..7371ec228 100644 --- a/include/keepkey/firmware/fsm.h +++ b/include/keepkey/firmware/fsm.h @@ -77,6 +77,7 @@ void fsm_msgWordAck(WordAck *msg); void fsm_msgCharacterAck(CharacterAck *msg); void fsm_msgApplyPolicies(ApplyPolicies *msg); +#ifndef BITCOIN_ONLY // ethereum void fsm_msgEthereumGetAddress(EthereumGetAddress *msg); void fsm_msgEthereumSignTx(EthereumSignTx *msg); @@ -118,6 +119,8 @@ void fsm_msgMayachainGetAddress(const MayachainGetAddress *msg); void fsm_msgMayachainSignTx(const MayachainSignTx *msg); void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg); +#endif // BITCOIN_ONLY + #if DEBUG_LINK // void fsm_msgDebugLinkDecision(DebugLinkDecision *msg); void fsm_msgDebugLinkGetState(DebugLinkGetState *msg); diff --git a/include/keepkey/firmware/mayachain.h b/include/keepkey/firmware/mayachain.h index 6c2cf2b59..72aa4b224 100644 --- a/include/keepkey/firmware/mayachain.h +++ b/include/keepkey/firmware/mayachain.h @@ -2,7 +2,7 @@ #define KEEPKEY_FIRMWARE_MAYACHAIN_H #include "messages.pb.h" -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include #include diff --git a/include/keepkey/firmware/nano.h b/include/keepkey/firmware/nano.h index edfe1f605..3bdfae8c5 100644 --- a/include/keepkey/firmware/nano.h +++ b/include/keepkey/firmware/nano.h @@ -2,8 +2,8 @@ #define KEEPKEY_FIRMWARE_NANO_H #include "keepkey/transport/interface.h" -#include "trezor/crypto/bip32.h" -#include "trezor/crypto/nano.h" +#include "hwcrypto/crypto/bip32.h" +#include "hwcrypto/crypto/nano.h" #include #include diff --git a/include/keepkey/firmware/osmosis.h b/include/keepkey/firmware/osmosis.h index 9a3f39498..5348e7015 100644 --- a/include/keepkey/firmware/osmosis.h +++ b/include/keepkey/firmware/osmosis.h @@ -2,7 +2,7 @@ #define KEEPKEY_FIRMWARE_OSMOSIS_H #include "messages.pb.h" -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include #include diff --git a/include/keepkey/firmware/ripple.h b/include/keepkey/firmware/ripple.h index d4230a4d4..fbd69889e 100644 --- a/include/keepkey/firmware/ripple.h +++ b/include/keepkey/firmware/ripple.h @@ -20,7 +20,7 @@ #ifndef KEEPKEY_FIRMWARE_RIPPLE_H #define KEEPKEY_FIRMWARE_RIPPLE_H -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include "messages-ripple.pb.h" @@ -31,6 +31,8 @@ #define RIPPLE_FLAG_FULLY_CANONICAL 0x80000000 +#define MAX_RIPPLE_ADDR_SIZE 36 + typedef enum { RFT_INT16 = 1, RFT_INT32 = 2, @@ -57,7 +59,7 @@ extern const RippleFieldMapping RFM_lastLedgerSequence; extern const RippleFieldMapping RFM_destinationTag; bool ripple_getAddress(const uint8_t public_key[33], - char address[MAX_ADDR_SIZE]); + char address[MAX_RIPPLE_ADDR_SIZE]); void ripple_formatAmount(char *buf, size_t len, uint64_t amount); diff --git a/include/keepkey/firmware/ripple_base58.h b/include/keepkey/firmware/ripple_base58.h index 98c9e83fe..7f825b6a6 100644 --- a/include/keepkey/firmware/ripple_base58.h +++ b/include/keepkey/firmware/ripple_base58.h @@ -24,8 +24,8 @@ #ifndef KEEPKEY_FIRMWARE_RIPPLE_BASE58_H #define KEEPKEY_FIRMWARE_RIPPLE_BASE58_H -#include "trezor/crypto/hasher.h" -#include "trezor/crypto/options.h" +#include "hwcrypto/crypto/hasher.h" +#include "hwcrypto/crypto/options.h" #include #include diff --git a/include/keepkey/firmware/signing.h b/include/keepkey/firmware/signing.h index 1295d1984..6c5202ba2 100644 --- a/include/keepkey/firmware/signing.h +++ b/include/keepkey/firmware/signing.h @@ -20,7 +20,7 @@ #ifndef SIGNING_H #define SIGNING_H -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include "keepkey/transport/interface.h" #include diff --git a/include/keepkey/firmware/signtx_tendermint.h b/include/keepkey/firmware/signtx_tendermint.h index 8933e2d02..6ad3dfd3d 100644 --- a/include/keepkey/firmware/signtx_tendermint.h +++ b/include/keepkey/firmware/signtx_tendermint.h @@ -2,7 +2,7 @@ #define KEEPKEY_FIRMWARE_SIGNTXTENDERMINT_H #include "messages.pb.h" -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include #include diff --git a/include/keepkey/firmware/storage.h b/include/keepkey/firmware/storage.h index 185051e17..6ca5b3be0 100644 --- a/include/keepkey/firmware/storage.h +++ b/include/keepkey/firmware/storage.h @@ -21,7 +21,7 @@ #ifndef STORAGE_H #define STORAGE_H -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include "keepkey/board/memory.h" #include "keepkey/firmware/authenticator.h" diff --git a/include/keepkey/firmware/tendermint.h b/include/keepkey/firmware/tendermint.h index 586e85879..b069a98cd 100644 --- a/include/keepkey/firmware/tendermint.h +++ b/include/keepkey/firmware/tendermint.h @@ -1,7 +1,7 @@ #ifndef KEEPKEY_FIRMWARE_TENDERMINT_H #define KEEPKEY_FIRMWARE_TENDERMINT_H -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include #include diff --git a/include/keepkey/firmware/thorchain.h b/include/keepkey/firmware/thorchain.h index f88baff28..ed2df0e24 100644 --- a/include/keepkey/firmware/thorchain.h +++ b/include/keepkey/firmware/thorchain.h @@ -2,7 +2,7 @@ #define KEEPKEY_FIRMWARE_THORCHAIN_H #include "messages.pb.h" -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/bip32.h" #include #include diff --git a/include/keepkey/firmware/transaction.h b/include/keepkey/firmware/transaction.h index c49ef9d13..5652ff13e 100644 --- a/include/keepkey/firmware/transaction.h +++ b/include/keepkey/firmware/transaction.h @@ -20,8 +20,8 @@ #ifndef TRANSACTION_H #define TRANSACTION_H -#include "trezor/crypto/sha2.h" -#include "trezor/crypto/bip32.h" +#include "hwcrypto/crypto/sha2.h" +#include "hwcrypto/crypto/bip32.h" #include "keepkey/transport/interface.h" #include diff --git a/include/keepkey/transport/interface.h b/include/keepkey/transport/interface.h index ffebf205d..20b2c0573 100644 --- a/include/keepkey/transport/interface.h +++ b/include/keepkey/transport/interface.h @@ -20,9 +20,15 @@ #ifndef INTERFACE_H #define INTERFACE_H +#include "types.pb.h" // Allow this file to be used from C++ by renaming an unfortunately named field: #define delete del #include "messages.pb.h" +#undef delete + +#ifndef BITCOIN_ONLY +// Allow this file to be used from C++ by renaming an unfortunately named field: +#define delete del #include "messages-nano.pb.h" #undef delete @@ -36,7 +42,8 @@ #include "messages-thorchain.pb.h" #include "messages-mayachain.pb.h" -#include "types.pb.h" +#endif // BITCOIN_ONLY + #include "trezor_transport.h" #ifndef EMULATOR diff --git a/include/keepkey/variant/keepkey.h b/include/keepkey/variant/keepkey.h index 345008a8a..784b650fb 100644 --- a/include/keepkey/variant/keepkey.h +++ b/include/keepkey/variant/keepkey.h @@ -1,16 +1,51 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2025 markrypto + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + #ifndef KEEPKEY_VARIANT_KEEPKEY_H #define KEEPKEY_VARIANT_KEEPKEY_H #include "keepkey/board/variant.h" -#define VARIANTINFO_KEEPKEY \ - .version = 1, .name = "KeepKey", .logo = &kk_logo, \ - .logo_reversed = &kk_logo_reversed, \ - .screensaver_timeout = ONE_SEC * 60 * 10, .screensaver = &kk_screensaver, +#ifdef BITCOIN_ONLY +// use the bitcoin-only logo +#define VARIANTINFO_KEEPKEY \ +.version = 1, .name = "KeepKeyBTC", .logo = &kkbtc_logo, \ +.logo_reversed = &kkbtc_logo_reversed, \ +.screensaver_timeout = ONE_SEC * 60 * 10, .screensaver = &kkbtc_screensaver, + +extern const VariantInfo variant_keepkey; +extern const VariantAnimation kkbtc_logo; +extern const VariantAnimation kkbtc_logo_reversed; +extern const VariantAnimation kkbtc_screensaver; + +#else + +#define VARIANTINFO_KEEPKEY \ +.version = 1, .name = "KeepKey", .logo = &kk_logo, \ +.logo_reversed = &kk_logo_reversed, \ +.screensaver_timeout = ONE_SEC * 60 * 10, .screensaver = &kk_screensaver, extern const VariantInfo variant_keepkey; extern const VariantAnimation kk_logo; extern const VariantAnimation kk_logo_reversed; extern const VariantAnimation kk_screensaver; -#endif +#endif // BITCOIN_ONLY + +#endif \ No newline at end of file diff --git a/include/pb.h b/include/pb.h deleted file mode 100644 index ce36d512b..000000000 --- a/include/pb.h +++ /dev/null @@ -1,647 +0,0 @@ -/* Common parts of the nanopb library. Most of these are quite low-level - * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. - */ - -#ifndef PB_H_INCLUDED -#define PB_H_INCLUDED - -/***************************************************************** - * Nanopb compilation time options. You can change these here by * - * uncommenting the lines, or on the compiler command line. * - *****************************************************************/ - -/* Enable support for dynamically allocated fields */ -/* #define PB_ENABLE_MALLOC 1 */ - -/* Define this if your CPU / compiler combination does not support - * unaligned memory access to packed structures. */ -/* #define PB_NO_PACKED_STRUCTS 1 */ - -/* Increase the number of required fields that are tracked. - * A compiler warning will tell if you need this. */ -/* #define PB_MAX_REQUIRED_FIELDS 256 */ - -/* Add support for tag numbers > 255 and fields larger than 255 bytes. */ -/* #define PB_FIELD_16BIT 1 */ - -/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ -/* #define PB_FIELD_32BIT 1 */ - -/* Disable support for error messages in order to save some code space. */ -/* #define PB_NO_ERRMSG 1 */ - -/* Disable support for custom streams (support only memory buffers). */ -/* #define PB_BUFFER_ONLY 1 */ - -/* Switch back to the old-style callback function signature. - * This was the default until nanopb-0.2.1. */ -/* #define PB_OLD_CALLBACK_STYLE */ - -/* Don't encode scalar arrays as packed. This is only to be used when - * the decoder on the receiving side cannot process packed scalar arrays. - * Such example is older protobuf.js. */ -/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ - -/****************************************************************** - * You usually don't need to change anything below this line. * - * Feel free to look around and use the defined macros, though. * - ******************************************************************/ - -/* Version of the nanopb library. Just in case you want to check it in - * your own program. */ -#define NANOPB_VERSION nanopb - 0.3.9.4 - -/* Include all the system headers needed by nanopb. You will need the - * definitions of the following: - * - strlen, memcpy, memset functions - * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t - * - size_t - * - bool - * - * If you don't have the standard header files, you can instead provide - * a custom header that defines or includes all this. In that case, - * define PB_SYSTEM_HEADER to the path of this file. - */ -#ifdef PB_SYSTEM_HEADER -#include PB_SYSTEM_HEADER -#else -#include -#include -#include -#include - -#ifdef PB_ENABLE_MALLOC -#include -#endif -#endif - -/* Macro for defining packed structures (compiler dependent). - * This just reduces memory requirements, but is not required. - */ -#if defined(PB_NO_PACKED_STRUCTS) -/* Disable struct packing */ -#define PB_PACKED_STRUCT_START -#define PB_PACKED_STRUCT_END -#define pb_packed -#elif defined(__GNUC__) || defined(__clang__) -/* For GCC and clang */ -#define PB_PACKED_STRUCT_START -#define PB_PACKED_STRUCT_END -#define pb_packed __attribute__((packed)) -#elif defined(__ICCARM__) || defined(__CC_ARM) -/* For IAR ARM and Keil MDK-ARM compilers */ -#define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") -#define PB_PACKED_STRUCT_END _Pragma("pack(pop)") -#define pb_packed -#elif defined(_MSC_VER) && (_MSC_VER >= 1500) -/* For Microsoft Visual C++ */ -#define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) -#define PB_PACKED_STRUCT_END __pragma(pack(pop)) -#define pb_packed -#else -/* Unknown compiler */ -#define PB_PACKED_STRUCT_START -#define PB_PACKED_STRUCT_END -#define pb_packed -#endif - -/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ -#ifndef PB_UNUSED -#define PB_UNUSED(x) (void)(x) -#endif - -/* Compile-time assertion, used for checking compatible compilation options. - * If this does not work properly on your compiler, use - * #define PB_NO_STATIC_ASSERT to disable it. - * - * But before doing that, check carefully the error message / place where it - * comes from to see if the error has a real cause. Unfortunately the error - * message is not always very clear to read, but you can see the reason better - * in the place where the PB_STATIC_ASSERT macro was called. - */ -#ifndef PB_NO_STATIC_ASSERT -#ifndef PB_STATIC_ASSERT -#define PB_STATIC_ASSERT(COND, MSG) \ - typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, \ - __COUNTER__)[(COND) ? 1 : -1]; -#define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) \ - PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) -#define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) \ - pb_static_assertion_##MSG##LINE##COUNTER -#endif -#else -#define PB_STATIC_ASSERT(COND, MSG) -#endif - -/* Number of required fields to keep track of. */ -#ifndef PB_MAX_REQUIRED_FIELDS -#define PB_MAX_REQUIRED_FIELDS 64 -#endif - -#if PB_MAX_REQUIRED_FIELDS < 64 -#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). -#endif - -/* List of possible field types. These are used in the autogenerated code. - * Least-significant 4 bits tell the scalar type - * Most-significant 4 bits specify repeated/required/packed etc. - */ - -typedef uint_least8_t pb_type_t; - -/**** Field data types ****/ - -/* Numeric types */ -#define PB_LTYPE_BOOL 0x00 /* bool */ -#define PB_LTYPE_VARINT 0x01 /* int32, int64, enum, bool */ -#define PB_LTYPE_UVARINT 0x02 /* uint32, uint64 */ -#define PB_LTYPE_SVARINT 0x03 /* sint32, sint64 */ -#define PB_LTYPE_FIXED32 0x04 /* fixed32, sfixed32, float */ -#define PB_LTYPE_FIXED64 0x05 /* fixed64, sfixed64, double */ - -/* Marker for last packable field type. */ -#define PB_LTYPE_LAST_PACKABLE 0x05 - -/* Byte array with pre-allocated buffer. - * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ -#define PB_LTYPE_BYTES 0x06 - -/* String with pre-allocated buffer. - * data_size is the maximum length. */ -#define PB_LTYPE_STRING 0x07 - -/* Submessage - * submsg_fields is pointer to field descriptions */ -#define PB_LTYPE_SUBMESSAGE 0x08 - -/* Extension pseudo-field - * The field contains a pointer to pb_extension_t */ -#define PB_LTYPE_EXTENSION 0x09 - -/* Byte array with inline, pre-allocated byffer. - * data_size is the length of the inline, allocated buffer. - * This differs from PB_LTYPE_BYTES by defining the element as - * pb_byte_t[data_size] rather than pb_bytes_array_t. */ -#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0A - -/* Number of declared LTYPES */ -#define PB_LTYPES_COUNT 0x0B -#define PB_LTYPE_MASK 0x0F - -/**** Field repetition rules ****/ - -#define PB_HTYPE_REQUIRED 0x00 -#define PB_HTYPE_OPTIONAL 0x10 -#define PB_HTYPE_REPEATED 0x20 -#define PB_HTYPE_ONEOF 0x30 -#define PB_HTYPE_MASK 0x30 - -/**** Field allocation types ****/ - -#define PB_ATYPE_STATIC 0x00 -#define PB_ATYPE_POINTER 0x80 -#define PB_ATYPE_CALLBACK 0x40 -#define PB_ATYPE_MASK 0xC0 - -#define PB_ATYPE(x) ((x)&PB_ATYPE_MASK) -#define PB_HTYPE(x) ((x)&PB_HTYPE_MASK) -#define PB_LTYPE(x) ((x)&PB_LTYPE_MASK) - -/* Data type used for storing sizes of struct fields - * and array counts. - */ -#if defined(PB_FIELD_32BIT) -typedef uint32_t pb_size_t; -typedef int32_t pb_ssize_t; -#elif defined(PB_FIELD_16BIT) -typedef uint_least16_t pb_size_t; -typedef int_least16_t pb_ssize_t; -#else -typedef uint_least8_t pb_size_t; -typedef int_least8_t pb_ssize_t; -#endif -#define PB_SIZE_MAX ((pb_size_t)-1) - -/* Data type for storing encoded data and other byte streams. - * This typedef exists to support platforms where uint8_t does not exist. - * You can regard it as equivalent on uint8_t on other platforms. - */ -typedef uint_least8_t pb_byte_t; - -/* This structure is used in auto-generated constants - * to specify struct fields. - * You can change field sizes if you need structures - * larger than 256 bytes or field tags larger than 256. - * The compiler should complain if your .proto has such - * structures. Fix that by defining PB_FIELD_16BIT or - * PB_FIELD_32BIT. - */ -PB_PACKED_STRUCT_START -typedef struct pb_field_s pb_field_t; -struct pb_field_s { - pb_size_t tag; - pb_type_t type; - pb_size_t data_offset; /* Offset of field data, relative to previous field. */ - pb_ssize_t - size_offset; /* Offset of array size or has-boolean, relative to data */ - pb_size_t data_size; /* Data size in bytes for a single item */ - pb_size_t array_size; /* Maximum number of entries in array */ - - /* Field definitions for submessage - * OR default value for all other non-array, non-callback types - * If null, then field will zeroed. */ - const void *ptr; -} pb_packed; -PB_PACKED_STRUCT_END - -/* Make sure that the standard integer types are of the expected sizes. - * Otherwise fixed32/fixed64 fields can break. - * - * If you get errors here, it probably means that your stdint.h is not - * correct for your platform. - */ -#ifndef PB_WITHOUT_64BIT -PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) -#endif - -/* This structure is used for 'bytes' arrays. - * It has the number of bytes in the beginning, and after that an array. - * Note that actual structs used will have a different length of bytes array. - */ -#define PB_BYTES_ARRAY_T(n) \ - struct { \ - pb_size_t size; \ - pb_byte_t bytes[n]; \ - } -#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) \ - ((size_t)n + offsetof(pb_bytes_array_t, bytes)) - -struct pb_bytes_array_s { - pb_size_t size; - pb_byte_t bytes[1]; -}; -typedef struct pb_bytes_array_s pb_bytes_array_t; - -/* This structure is used for giving the callback function. - * It is stored in the message structure and filled in by the method that - * calls pb_decode. - * - * The decoding callback will be given a limited-length stream - * If the wire type was string, the length is the length of the string. - * If the wire type was a varint/fixed32/fixed64, the length is the length - * of the actual value. - * The function may be called multiple times (especially for repeated types, - * but also otherwise if the message happens to contain the field multiple - * times.) - * - * The encoding callback will receive the actual output stream. - * It should write all the data in one call, including the field tag and - * wire type. It can write multiple fields. - * - * The callback can be null if you want to skip a field. - */ -typedef struct pb_istream_s pb_istream_t; -typedef struct pb_ostream_s pb_ostream_t; -typedef struct pb_callback_s pb_callback_t; -struct pb_callback_s { -#ifdef PB_OLD_CALLBACK_STYLE - /* Deprecated since nanopb-0.2.1 */ - union { - bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg); - bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, - const void *arg); - } funcs; -#else - /* New function signature, which allows modifying arg contents in callback. */ - union { - bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); - bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg); - } funcs; -#endif - - /* Free arg for use by callback */ - void *arg; -}; - -/* Wire types. Library user needs these only in encoder callbacks. */ -typedef enum { - PB_WT_VARINT = 0, - PB_WT_64BIT = 1, - PB_WT_STRING = 2, - PB_WT_32BIT = 5 -} pb_wire_type_t; - -/* Structure for defining the handling of unknown/extension fields. - * Usually the pb_extension_type_t structure is automatically generated, - * while the pb_extension_t structure is created by the user. However, - * if you want to catch all unknown fields, you can also create a custom - * pb_extension_type_t with your own callback. - */ -typedef struct pb_extension_type_s pb_extension_type_t; -typedef struct pb_extension_s pb_extension_t; -struct pb_extension_type_s { - /* Called for each unknown field in the message. - * If you handle the field, read off all of its data and return true. - * If you do not handle the field, do not read anything and return true. - * If you run into an error, return false. - * Set to NULL for default handler. - */ - bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, - pb_wire_type_t wire_type); - - /* Called once after all regular fields have been encoded. - * If you have something to write, do so and return true. - * If you do not have anything to write, just return true. - * If you run into an error, return false. - * Set to NULL for default handler. - */ - bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); - - /* Free field for use by the callback. */ - const void *arg; -}; - -struct pb_extension_s { - /* Type describing the extension field. Usually you'll initialize - * this to a pointer to the automatically generated structure. */ - const pb_extension_type_t *type; - - /* Destination for the decoded data. This must match the datatype - * of the extension field. */ - void *dest; - - /* Pointer to the next extension handler, or NULL. - * If this extension does not match a field, the next handler is - * automatically called. */ - pb_extension_t *next; - - /* The decoder sets this to true if the extension was found. - * Ignored for encoding. */ - bool found; -}; - -/* Memory allocation functions to use. You can define pb_realloc and - * pb_free to custom functions if you want. */ -#ifdef PB_ENABLE_MALLOC -#ifndef pb_realloc -#define pb_realloc(ptr, size) realloc(ptr, size) -#endif -#ifndef pb_free -#define pb_free(ptr) free(ptr) -#endif -#endif - -/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ -#define PB_PROTO_HEADER_VERSION 30 - -/* These macros are used to declare pb_field_t's in the constant array. */ -/* Size of a structure member, in bytes. */ -#define pb_membersize(st, m) (sizeof((st *)0)->m) -/* Number of entries in an array. */ -#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) -/* Delta from start of one member to the start of another member. */ -#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) -/* Marks the end of the field list */ -#define PB_LAST_FIELD \ - { 0, (pb_type_t)0, 0, 0, 0, 0, 0 } - -/* Macros for filling in the data_offset field */ -/* data_offset for first field in a message */ -#define PB_DATAOFFSET_FIRST(st, m1, m2) (offsetof(st, m1)) -/* data_offset for subsequent fields */ -#define PB_DATAOFFSET_OTHER(st, m1, m2) \ - (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2)) -/* data offset for subsequent fields inside an union (oneof) */ -#define PB_DATAOFFSET_UNION(st, m1, m2) (PB_SIZE_MAX) -/* Choose first/other based on m1 == m2 (deprecated, remains for backwards - * compatibility) */ -#define PB_DATAOFFSET_CHOOSE(st, m1, m2) \ - (int)(offsetof(st, m1) == offsetof(st, m2) \ - ? PB_DATAOFFSET_FIRST(st, m1, m2) \ - : PB_DATAOFFSET_OTHER(st, m1, m2)) - -/* Required fields are the simplest. They just have delta (padding) from - * previous field end, and the size of the field. Pointer is used for - * submessages and default values. - */ -#define PB_REQUIRED_STATIC(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, fd, 0, \ - pb_membersize(st, m), 0, ptr \ - } - -/* Optional fields add the delta to the has_ variable. */ -#define PB_OPTIONAL_STATIC(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, fd, \ - pb_delta(st, has_##m, m), pb_membersize(st, m), 0, ptr \ - } - -#define PB_SINGULAR_STATIC(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, fd, 0, \ - pb_membersize(st, m), 0, ptr \ - } - -/* Repeated fields have a _count field and also the maximum number of entries. - */ -#define PB_REPEATED_STATIC(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | ltype, fd, \ - pb_delta(st, m##_count, m), pb_membersize(st, m[0]), \ - pb_arraysize(st, m), ptr \ - } - -/* Allocated fields carry the size of the actual data, not the pointer */ -#define PB_REQUIRED_POINTER(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_POINTER | PB_HTYPE_REQUIRED | ltype, fd, 0, \ - pb_membersize(st, m[0]), 0, ptr \ - } - -/* Optional fields don't need a has_ variable, as information would be redundant - */ -#define PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_POINTER | PB_HTYPE_OPTIONAL | ltype, fd, 0, \ - pb_membersize(st, m[0]), 0, ptr \ - } - -/* Same as optional fields*/ -#define PB_SINGULAR_POINTER(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_POINTER | PB_HTYPE_OPTIONAL | ltype, fd, 0, \ - pb_membersize(st, m[0]), 0, ptr \ - } - -/* Repeated fields have a _count field and a pointer to array of pointers */ -#define PB_REPEATED_POINTER(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_POINTER | PB_HTYPE_REPEATED | ltype, fd, \ - pb_delta(st, m##_count, m), pb_membersize(st, m[0]), 0, ptr \ - } - -/* Callbacks are much like required fields except with special datatype. */ -#define PB_REQUIRED_CALLBACK(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_CALLBACK | PB_HTYPE_REQUIRED | ltype, fd, 0, \ - pb_membersize(st, m), 0, ptr \ - } - -#define PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, fd, 0, \ - pb_membersize(st, m), 0, ptr \ - } - -#define PB_SINGULAR_CALLBACK(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, fd, 0, \ - pb_membersize(st, m), 0, ptr \ - } - -#define PB_REPEATED_CALLBACK(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, fd, 0, \ - pb_membersize(st, m), 0, ptr \ - } - -/* Optional extensions don't have the has_ field, as that would be redundant. - * Furthermore, the combination of OPTIONAL without has_ field is used - * for indicating proto3 style fields. Extensions exist in proto2 mode only, - * so they should be encoded according to proto2 rules. To avoid the conflict, - * extensions are marked as REQUIRED instead. - */ -#define PB_OPTEXT_STATIC(tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, 0, 0, \ - pb_membersize(st, m), 0, ptr \ - } - -#define PB_OPTEXT_POINTER(tag, st, m, fd, ltype, ptr) \ - PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr) - -#define PB_OPTEXT_CALLBACK(tag, st, m, fd, ltype, ptr) \ - PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) - -/* The mapping from protobuf types to LTYPEs is done using these macros. */ -#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL -#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES -#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 -#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT -#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT -#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 -#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 -#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 -#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT -#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT -#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE -#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 -#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 -#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT -#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT -#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING -#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT -#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT -#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION -#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES - -/* This is the actual macro used in field descriptions. - * It takes these arguments: - * - Field tag number - * - Field type: BOOL, BYTES, DOUBLE, ENUM, UENUM, FIXED32, FIXED64, - * FLOAT, INT32, INT64, MESSAGE, SFIXED32, SFIXED64 - * SINT32, SINT64, STRING, UINT32, UINT64 or EXTENSION - * - Field rules: REQUIRED, OPTIONAL or REPEATED - * - Allocation: STATIC, CALLBACK or POINTER - * - Placement: FIRST or OTHER, depending on if this is the first field in - * structure. - * - Message name - * - Field name - * - Previous field name (or field name again for first field) - * - Pointer to default value or submsg fields. - */ - -#define PB_FIELD(tag, type, rules, allocation, placement, message, field, \ - prevfield, ptr) \ - PB_##rules##_##allocation( \ - tag, message, field, \ - PB_DATAOFFSET_##placement(message, field, prevfield), \ - PB_LTYPE_MAP_##type, ptr) - -/* Field description for repeated static fixed count fields.*/ -#define PB_REPEATED_FIXED_COUNT(tag, type, placement, message, field, \ - prevfield, ptr) \ - { \ - tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | PB_LTYPE_MAP_##type, \ - PB_DATAOFFSET_##placement(message, field, prevfield), 0, \ - pb_membersize(message, field[0]), pb_arraysize(message, field), ptr \ - } - -/* Field description for oneof fields. This requires taking into account the - * union name also, that's why a separate set of macros is needed. - */ -#define PB_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, fd, \ - pb_delta(st, which_##u, u.m), pb_membersize(st, u.m), 0, ptr \ - } - -#define PB_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, fd, \ - pb_delta(st, which_##u, u.m), pb_membersize(st, u.m[0]), 0, ptr \ - } - -#define PB_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, \ - message, field, prevfield, ptr) \ - PB_ONEOF_##allocation( \ - union_name, tag, message, field, \ - PB_DATAOFFSET_##placement(message, union_name.field, prevfield), \ - PB_LTYPE_MAP_##type, ptr) - -#define PB_ANONYMOUS_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, fd, \ - pb_delta(st, which_##u, m), pb_membersize(st, m), 0, ptr \ - } - -#define PB_ANONYMOUS_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \ - { \ - tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, fd, \ - pb_delta(st, which_##u, m), pb_membersize(st, m[0]), 0, ptr \ - } - -#define PB_ANONYMOUS_ONEOF_FIELD(union_name, tag, type, rules, allocation, \ - placement, message, field, prevfield, ptr) \ - PB_ANONYMOUS_ONEOF_##allocation( \ - union_name, tag, message, field, \ - PB_DATAOFFSET_##placement(message, field, prevfield), \ - PB_LTYPE_MAP_##type, ptr) - -/* These macros are used for giving out error messages. - * They are mostly a debugging aid; the main error information - * is the true/false return value from functions. - * Some code space can be saved by disabling the error - * messages if not used. - * - * PB_SET_ERROR() sets the error message if none has been set yet. - * msg must be a constant string literal. - * PB_GET_ERROR() always returns a pointer to a string. - * PB_RETURN_ERROR() sets the error and returns false from current - * function. - */ -#ifdef PB_NO_ERRMSG -#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) -#define PB_GET_ERROR(stream) "(errmsg disabled)" -#else -#define PB_SET_ERROR(stream, msg) \ - (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) -#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") -#endif - -#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false - -#endif diff --git a/include/pb_common.h b/include/pb_common.h deleted file mode 100644 index 397feda76..000000000 --- a/include/pb_common.h +++ /dev/null @@ -1,43 +0,0 @@ -/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. - * These functions are rarely needed by applications directly. - */ - -#ifndef PB_COMMON_H_INCLUDED -#define PB_COMMON_H_INCLUDED - -#include "pb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Iterator for pb_field_t list */ -struct pb_field_iter_s { - const pb_field_t *start; /* Start of the pb_field_t array */ - const pb_field_t *pos; /* Current position of the iterator */ - unsigned required_field_index; /* Zero-based index that counts only the - required fields */ - void *dest_struct; /* Pointer to start of the structure */ - void *pData; /* Pointer to current field value */ - void *pSize; /* Pointer to count/has field */ -}; -typedef struct pb_field_iter_s pb_field_iter_t; - -/* Initialize the field iterator structure to beginning. - * Returns false if the message type is empty. */ -bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_field_t *fields, - void *dest_struct); - -/* Advance the iterator to the next field. - * Returns false when the iterator wraps back to the first field. */ -bool pb_field_iter_next(pb_field_iter_t *iter); - -/* Advance the iterator until it points at a field with the given tag. - * Returns false if no such field exists. */ -bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/include/pb_decode.h b/include/pb_decode.h deleted file mode 100644 index a4327d2df..000000000 --- a/include/pb_decode.h +++ /dev/null @@ -1,181 +0,0 @@ -/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. - * The main function is pb_decode. You also need an input stream, and the - * field descriptions created by nanopb_generator.py. - */ - -#ifndef PB_DECODE_H_INCLUDED -#define PB_DECODE_H_INCLUDED - -#include "pb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Structure for defining custom input streams. You will need to provide - * a callback function to read the bytes from your storage, which can be - * for example a file or a network socket. - * - * The callback must conform to these rules: - * - * 1) Return false on IO errors. This will cause decoding to abort. - * 2) You can use state to store your own data (e.g. buffer pointer), - * and rely on pb_read to verify that no-body reads past bytes_left. - * 3) Your callback may be used with substreams, in which case bytes_left - * is different than from the main stream. Don't use bytes_left to compute - * any pointers. - */ -struct pb_istream_s { -#ifdef PB_BUFFER_ONLY - /* Callback pointer is not used in buffer-only configuration. - * Having an int pointer here allows binary compatibility but - * gives an error if someone tries to assign callback function. - */ - int *callback; -#else - bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); -#endif - - void *state; /* Free field for use by callback implementation */ - size_t bytes_left; - -#ifndef PB_NO_ERRMSG - const char *errmsg; -#endif -}; - -/*************************** - * Main decoding functions * - ***************************/ - -/* Decode a single protocol buffers message from input stream into a C - * structure. Returns true on success, false on any failure. The actual struct - * pointed to by dest must match the description in fields. Callback fields of - * the destination structure must be initialized by caller. All other fields - * will be initialized by this function. - * - * Example usage: - * MyMessage msg = {}; - * uint8_t buffer[64]; - * pb_istream_t stream; - * - * // ... read some data into buffer ... - * - * stream = pb_istream_from_buffer(buffer, count); - * pb_decode(&stream, MyMessage_fields, &msg); - */ -bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct); - -/* Same as pb_decode, except does not initialize the destination structure - * to default values. This is slightly faster if you need no default values - * and just do memset(struct, 0, sizeof(struct)) yourself. - * - * This can also be used for 'merging' two messages, i.e. update only the - * fields that exist in the new message. - * - * Note: If this function returns with an error, it will not release any - * dynamically allocated fields. You will need to call pb_release() yourself. - */ -bool pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct); - -/* Same as pb_decode, except expects the stream to start with the message size - * encoded as varint. Corresponds to parseDelimitedFrom() in Google's - * protobuf API. - */ -bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct); - -/* Same as pb_decode_delimited, except that it does not initialize the - * destination structure. See pb_decode_noinit - */ -bool pb_decode_delimited_noinit(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct); - -/* Same as pb_decode, except allows the message to be terminated with a null - * byte. NOTE: Until nanopb-0.4.0, pb_decode() also allows null-termination. - * This behaviour is not supported in most other protobuf implementations, so - * pb_decode_delimited() is a better option for compatibility. - */ -bool pb_decode_nullterminated(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct); - -#ifdef PB_ENABLE_MALLOC -/* Release any allocated pointer fields. If you use dynamic allocation, you - * should call this for any successfully decoded message when you are done with - * it. If pb_decode() returns with an error, the message is already released. - */ -void pb_release(const pb_field_t fields[], void *dest_struct); -#endif - -/************************************** - * Functions for manipulating streams * - **************************************/ - -/* Create an input stream for reading from a memory buffer. - * - * Alternatively, you can use a custom stream that reads directly from e.g. - * a file or a network socket. - */ -pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize); - -/* Function to read from a pb_istream_t. You can use this if you need to - * read some custom header data, or to read data in field callbacks. - */ -bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); - -/************************************************ - * Helper functions for writing field callbacks * - ************************************************/ - -/* Decode the tag for the next field in the stream. Gives the wire type and - * field tag. At end of the message, returns false and sets eof to true. */ -bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, - uint32_t *tag, bool *eof); - -/* Skip the field payload data, given the wire type. */ -bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); - -/* Decode an integer in the varint format. This works for enum, int32, - * int64, uint32 and uint64 field types. */ -#ifndef PB_WITHOUT_64BIT -bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); -#else -#define pb_decode_varint pb_decode_varint32 -#endif - -/* Decode an integer in the varint format. This works for enum, int32, - * and uint32 field types. */ -bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); - -/* Decode a bool value in varint format. */ -bool pb_decode_bool(pb_istream_t *stream, bool *dest); - -/* Decode an integer in the zig-zagged svarint format. This works for sint32 - * and sint64. */ -#ifndef PB_WITHOUT_64BIT -bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); -#else -bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); -#endif - -/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to - * a 4-byte wide C variable. */ -bool pb_decode_fixed32(pb_istream_t *stream, void *dest); - -#ifndef PB_WITHOUT_64BIT -/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to - * a 8-byte wide C variable. */ -bool pb_decode_fixed64(pb_istream_t *stream, void *dest); -#endif - -/* Make a limited-length substream for reading a PB_WT_STRING field. */ -bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); -bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/include/pb_encode.h b/include/pb_encode.h deleted file mode 100644 index cb2eba1aa..000000000 --- a/include/pb_encode.h +++ /dev/null @@ -1,177 +0,0 @@ -/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. - * The main function is pb_encode. You also need an output stream, and the - * field descriptions created by nanopb_generator.py. - */ - -#ifndef PB_ENCODE_H_INCLUDED -#define PB_ENCODE_H_INCLUDED - -#include "pb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Structure for defining custom output streams. You will need to provide - * a callback function to write the bytes to your storage, which can be - * for example a file or a network socket. - * - * The callback must conform to these rules: - * - * 1) Return false on IO errors. This will cause encoding to abort. - * 2) You can use state to store your own data (e.g. buffer pointer). - * 3) pb_write will update bytes_written after your callback runs. - * 4) Substreams will modify max_size and bytes_written. Don't use them - * to calculate any pointers. - */ -struct pb_ostream_s { -#ifdef PB_BUFFER_ONLY - /* Callback pointer is not used in buffer-only configuration. - * Having an int pointer here allows binary compatibility but - * gives an error if someone tries to assign callback function. - * Also, NULL pointer marks a 'sizing stream' that does not - * write anything. - */ - int *callback; -#else - bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); -#endif - void *state; /* Free field for use by callback implementation. */ - size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */ - size_t bytes_written; /* Number of bytes written so far. */ - -#ifndef PB_NO_ERRMSG - const char *errmsg; -#endif -}; - -/*************************** - * Main encoding functions * - ***************************/ - -/* Encode a single protocol buffers message from C structure into a stream. - * Returns true on success, false on any failure. - * The actual struct pointed to by src_struct must match the description in - * fields. All required fields in the struct are assumed to have been filled in. - * - * Example usage: - * MyMessage msg = {}; - * uint8_t buffer[64]; - * pb_ostream_t stream; - * - * msg.field1 = 42; - * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); - * pb_encode(&stream, MyMessage_fields, &msg); - */ -bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], - const void *src_struct); - -/* Same as pb_encode, but prepends the length of the message as a varint. - * Corresponds to writeDelimitedTo() in Google's protobuf API. - */ -bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], - const void *src_struct); - -/* Same as pb_encode, but appends a null byte to the message for termination. - * NOTE: This behaviour is not supported in most other protobuf implementations, - * so pb_encode_delimited() is a better option for compatibility. - */ -bool pb_encode_nullterminated(pb_ostream_t *stream, const pb_field_t fields[], - const void *src_struct); - -/* Encode the message to get the size of the encoded data, but do not store - * the data. */ -bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], - const void *src_struct); - -/************************************** - * Functions for manipulating streams * - **************************************/ - -/* Create an output stream for writing into a memory buffer. - * The number of bytes written can be found in stream.bytes_written after - * encoding the message. - * - * Alternatively, you can use a custom stream that writes directly to e.g. - * a file or a network socket. - */ -pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); - -/* Pseudo-stream for measuring the size of a message without actually storing - * the encoded data. - * - * Example usage: - * MyMessage msg = {}; - * pb_ostream_t stream = PB_OSTREAM_SIZING; - * pb_encode(&stream, MyMessage_fields, &msg); - * printf("Message size is %d\n", stream.bytes_written); - */ -#ifndef PB_NO_ERRMSG -#define PB_OSTREAM_SIZING \ - { 0, 0, 0, 0, 0 } -#else -#define PB_OSTREAM_SIZING \ - { 0, 0, 0, 0 } -#endif - -/* Function to write into a pb_ostream_t stream. You can use this if you need - * to append or prepend some custom headers to the message. - */ -bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); - -/************************************************ - * Helper functions for writing field callbacks * - ************************************************/ - -/* Encode field header based on type and field number defined in the field - * structure. Call this from the callback before writing out field contents. */ -bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field); - -/* Encode field header by manually specifing wire type. You need to use this - * if you want to write out packed arrays from a callback field. */ -bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, - uint32_t field_number); - -/* Encode an integer in the varint format. - * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ -#ifndef PB_WITHOUT_64BIT -bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); -#else -bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); -#endif - -/* Encode an integer in the zig-zagged svarint format. - * This works for sint32 and sint64. */ -#ifndef PB_WITHOUT_64BIT -bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); -#else -bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); -#endif - -/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ -bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, - size_t size); - -/* Encode a fixed32, sfixed32 or float value. - * You need to pass a pointer to a 4-byte wide C variable. */ -bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); - -#ifndef PB_WITHOUT_64BIT -/* Encode a fixed64, sfixed64 or double value. - * You need to pass a pointer to a 8-byte wide C variable. */ -bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); -#endif - -/* Encode a submessage field. - * You need to pass the pb_field_t array and pointer to struct, just like - * with pb_encode(). This internally encodes the submessage twice, first to - * calculate message size and then to actually write it out. - */ -bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], - const void *src_struct); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/include/trezor/crypto b/include/trezor/crypto deleted file mode 120000 index 5bac4fafe..000000000 --- a/include/trezor/crypto +++ /dev/null @@ -1 +0,0 @@ -../../deps/crypto/trezor-firmware/crypto \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6f5692431..bc5d6ff4d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -4,3 +4,30 @@ add_subdirectory(firmware) add_subdirectory(rand) add_subdirectory(transport) add_subdirectory(variant) + +# The nanopb generator is pre-built in the docker container. The kk build has dependencies on these +# files and were originally just copy pasted to these directories from the nanopb repo. That has +# caused an unfortunate nanopb version dependency, i.e., these files need to be copied from the same +# version of nanopb that is in the container. +# +# The cmake code here is copying the files out of the nanopb directory in the container for the kk build. +# These files must then be deleted at the end of the build because they are owned by root and will +# cause problems with the keepkey-firmware repo. +# +# A better way to implement this is to vendorize the nanopb dependency. However, this will force an +# update to a bunch of kk source files. Future project. +set(nanopb_sources + ${NANOPB_DIR}/pb_common.c + ${NANOPB_DIR}/pb_decode.c + ${NANOPB_DIR}/pb_encode.c +) + +set(nanopb_inc + ${NANOPB_DIR}/pb_common.h + ${NANOPB_DIR}/pb_decode.h + ${NANOPB_DIR}/pb_encode.h + ${NANOPB_DIR}/pb.h +) + +file(COPY ${nanopb_sources} DESTINATION ${CMAKE_SOURCE_DIR}/lib/transport) +file(COPY ${nanopb_inc} DESTINATION ${CMAKE_SOURCE_DIR}/include/) diff --git a/lib/board/CMakeLists.txt b/lib/board/CMakeLists.txt index 10c30b424..fff76eb8f 100644 --- a/lib/board/CMakeLists.txt +++ b/lib/board/CMakeLists.txt @@ -8,7 +8,6 @@ set(sources font.c keepkey_board.c keepkey_button.c - keepkey_display.c keepkey_flash.c keepkey_leds.c keepkey_usart.c @@ -26,6 +25,20 @@ set(sources util.c variant.c) +if("${DEVDEBUG}" STREQUAL "true") + set(sources ${sources} + keepkey_spidisplay.c) + if("${TWODISP}" STREQUAL "true") + set(sources ${sources} + ssd1351/ssd1351.c + ssd1351/fonts.c) + endif() + +else() + set(sources ${sources} + keepkey_display.c) +endif() + check_symbol_exists(strlcpy "string.h" KK_HAVE_STRLCPY) if(NOT KK_HAVE_STRLCPY) set(sources ${sources} strlcpy.c) diff --git a/lib/board/check_bootloader.c b/lib/board/check_bootloader.c index 945e4ebe2..7a410ee22 100644 --- a/lib/board/check_bootloader.c +++ b/lib/board/check_bootloader.c @@ -23,7 +23,7 @@ #include #endif -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/sha2.h" #include "keepkey/board/keepkey_board.h" #include "keepkey/board/layout.h" #include "keepkey/board/memory.h" diff --git a/lib/board/common.c b/lib/board/common.c index 06e4518d6..92056e0b8 100644 --- a/lib/board/common.c +++ b/lib/board/common.c @@ -21,8 +21,8 @@ #include "keepkey/board/otp.h" #include "keepkey/rand/rng.h" -#include "trezor/crypto/hmac_drbg.h" -#include "trezor/crypto/rand.h" +#include "hwcrypto/crypto/hmac_drbg.h" +#include "hwcrypto/crypto/rand.h" #ifndef EMULATOR #include diff --git a/lib/board/confirm_sm.c b/lib/board/confirm_sm.c index 534930f06..219d9a333 100644 --- a/lib/board/confirm_sm.c +++ b/lib/board/confirm_sm.c @@ -25,12 +25,17 @@ #include "keepkey/board/confirm_sm.h" #include "keepkey/board/usb.h" #include "keepkey/board/supervise.h" -#include "trezor/crypto/memzero.h" +#include "hwcrypto/crypto/memzero.h" #ifndef EMULATOR #include + +#ifdef DEV_DEBUG +#include "keepkey/board/pin.h" #endif +#endif // EMULATOR + #include #include #include @@ -49,6 +54,10 @@ static CONFIDENTIAL char strbuf[BODY_CHAR_MAX]; /// Handler for push button being pressed. /// \param context current state context. static void handle_screen_press(void *context) { + #ifdef DEV_DEBUG + CLEAR_PIN(SCOPE_PIN); + #endif + assert(context != NULL); StateInfo *si = (StateInfo *)context; @@ -82,6 +91,10 @@ static void handle_screen_release(void *context) { case CONFIRMED: si->active_layout = LAYOUT_FINISHED; si->display_state = FINISHED; + #ifdef DEV_DEBUG + SET_PIN(SCOPE_PIN); + #endif + break; default: diff --git a/lib/board/keepkey_board.c b/lib/board/keepkey_board.c index c2e83fa57..0a0f61c33 100644 --- a/lib/board/keepkey_board.c +++ b/lib/board/keepkey_board.c @@ -22,8 +22,14 @@ #include #include #include + +#ifdef DEV_DEBUG +#include +#include +#else #include #include +#endif #include #include #endif @@ -35,8 +41,10 @@ #include #include +#ifndef EMULATOR /* Stack smashing protector (SSP) canary value storage */ uintptr_t __stack_chk_guard; +#endif #ifdef EMULATOR /** diff --git a/lib/board/keepkey_button.c b/lib/board/keepkey_button.c index b0b453faa..cf4d647b8 100644 --- a/lib/board/keepkey_button.c +++ b/lib/board/keepkey_button.c @@ -21,7 +21,11 @@ #include #include #include +#ifdef DEV_DEBUG +#include +#else #include +#endif #include #endif @@ -38,10 +42,15 @@ static void *on_release_handler_context = NULL; static void *on_press_handler_context = NULL; #ifndef EMULATOR -static const uint16_t BUTTON_PIN = GPIO7; static const uint32_t BUTTON_PORT = GPIOB; +#ifdef DEV_DEBUG +static const uint16_t BUTTON_PIN = GPIO9; +static const uint32_t BUTTON_EXTI = EXTI9; +#else +static const uint16_t BUTTON_PIN = GPIO7; static const uint32_t BUTTON_EXTI = EXTI7; -#endif +#endif // DEV_DEBUG +#endif // EMULATOR void kk_keepkey_button_init(void) { on_press_handler = NULL; @@ -69,6 +78,7 @@ void keepkey_button_init(void) { #ifndef EMULATOR gpio_mode_setup(BUTTON_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, BUTTON_PIN); + /* Set up port B */ /* Configure the EXTI subsystem. */ exti_select_source(BUTTON_EXTI, GPIOB); @@ -141,7 +151,9 @@ bool keepkey_button_down(void) { return !keepkey_button_up(); } void buttonisr_usr(void) { #ifndef EMULATOR - if (gpio_get(BUTTON_PORT, BUTTON_PIN) & BUTTON_PIN) { + uint16_t gpState = gpio_get(BUTTON_PORT, BUTTON_PIN) & BUTTON_PIN; + + if (gpState) { if (on_release_handler) { on_release_handler(on_release_handler_context); } diff --git a/lib/board/keepkey_display.c b/lib/board/keepkey_display.c index 1a0fe38ab..7805217b0 100644 --- a/lib/board/keepkey_display.c +++ b/lib/board/keepkey_display.c @@ -30,17 +30,6 @@ #pragma GCC push_options #pragma GCC optimize("-O3") -#ifndef EMULATOR -static const Pin nOE_PIN = {GPIOA, GPIO8}; -static const Pin nWE_PIN = {GPIOA, GPIO9}; -static const Pin nDC_PIN = {GPIOB, GPIO1}; - -static const Pin nSEL_PIN = {GPIOA, GPIO10}; -static const Pin nRESET_PIN = {GPIOB, GPIO5}; - -static const Pin BACKLIGHT_PWR_PIN = {GPIOB, GPIO0}; -#endif - static uint8_t canvas_buffer[KEEPKEY_DISPLAY_HEIGHT * KEEPKEY_DISPLAY_WIDTH]; static Canvas canvas; bool constant_power = false; diff --git a/lib/board/keepkey_flash.c b/lib/board/keepkey_flash.c index 5f92bb732..788e983ec 100644 --- a/lib/board/keepkey_flash.c +++ b/lib/board/keepkey_flash.c @@ -32,8 +32,8 @@ #include "keepkey/board/supervise.h" #include "keepkey/board/util.h" #include "keepkey/rand/rng.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/rand.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/rand.h" #include #include diff --git a/lib/board/keepkey_leds.c b/lib/board/keepkey_leds.c index c7c21ee86..d081e4e61 100644 --- a/lib/board/keepkey_leds.c +++ b/lib/board/keepkey_leds.c @@ -23,9 +23,14 @@ #include "keepkey/board/pin.h" #ifndef EMULATOR +#ifdef DEV_DEBUG +static const Pin GREEN_LED = {GPIOC, GPIO1}; +static const Pin RED_LED = {GPIOB, GPIO8}; +#else static const Pin GREEN_LED = {GPIOC, GPIO14}; -static const Pin RED_LED = {GPIOC, GPIO15}; -#endif +static const Pin RED_LED = {GPIOB, GPIO8}; +#endif // DEV_DEBUG +#endif // EMULATOR /* * keepkey_leds_init() - Initialize gpios for LEDs @@ -38,7 +43,7 @@ static const Pin RED_LED = {GPIOC, GPIO15}; void keepkey_leds_init(void) { #ifndef EMULATOR pin_init_output(&GREEN_LED, PUSH_PULL_MODE, NO_PULL_MODE); - pin_init_output(&RED_LED, PUSH_PULL_MODE, NO_PULL_MODE); + // pin_init_output(&RED_LED, PUSH_PULL_MODE, NO_PULL_MODE); led_func(CLR_GREEN_LED); led_func(CLR_RED_LED); diff --git a/lib/board/keepkey_spidisplay.c b/lib/board/keepkey_spidisplay.c new file mode 100644 index 000000000..519568195 --- /dev/null +++ b/lib/board/keepkey_spidisplay.c @@ -0,0 +1,466 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2025 markrypt0 + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef EMULATOR +#include +#include +#include +#endif + +#include "keepkey/board/keepkey_display.h" +#include "keepkey/board/pin.h" +#include "keepkey/board/timer.h" +#include "keepkey/board/supervise.h" +#include "keepkey/board/keepkey_leds.h" + +#pragma GCC push_options +#pragma GCC optimize("-O3") + + +static uint8_t canvas_buffer[KEEPKEY_DISPLAY_HEIGHT * KEEPKEY_DISPLAY_WIDTH]; +static Canvas canvas; +bool constant_power = false; + +static void spi_setup(void) { + #ifndef EMULATOR + + gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, + GPIO4 | GPIO5 | GPIO7); + + // enable SPI 1 for OLED display + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO4 | GPIO5 | GPIO7); + + gpio_set_af(GPIOA, GPIO_AF5, GPIO4 | GPIO5 | GPIO7); + + + // enable SPI clock + rcc_periph_clock_enable(RCC_SPI1); + +/* +int spi_init_master ( +uint32_t spi, +uint32_t br, +uint32_t cpol, +uint32_t cpha, +uint32_t dff, +uint32_t lsbfirst +) + +Parameters +[in] spi Unsigned int32. SPI peripheral identifier SPI Register base address. +[in] br Unsigned int32. Baudrate SPI peripheral baud rates. +[in] cpol Unsigned int32. Clock polarity SPI clock polarity. +[in] cpha Unsigned int32. Clock Phase SPI clock phase. +[in] dff Unsigned int32. Data frame format 8/16 bits SPI data frame format. +[in] lsbfirst Unsigned int32. Frame format lsb/msb first SPI lsb/msb first. + +*/ + spi_init_master( + SPI1, + SPI_CR1_BAUDRATE_FPCLK_DIV_4, + SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLE, + SPI_CR1_CPHA_CLK_TRANSITION_2, + SPI_CR1_DFF_8BIT, + SPI_CR1_MSBFIRST); + + spi_enable_ss_output(SPI1); + spi_enable(SPI1); + +#endif // EMULATOR +} + + +/* + * display_write_reg() - Write data to display register + * + * INPUT + * - reg: display register value + * OUTPUT + * none + */ +static void display_write_reg(uint8_t reg) { +#ifndef EMULATOR + + // /* Set nOLED_SEL low */ + CLEAR_PIN(nSEL_PIN); + + /* Set nDC low */ + CLEAR_PIN(nDC_PIN); + + spi_send(SPI1, (uint16_t)reg); + delay_us(10); + + /* Set nOLED_SEL high */ + SET_PIN(nSEL_PIN); + +#endif +} + +/* + * display_reset() - Reset display io port + * + * INPUT + * none + * OUTPUT + * none + */ +static void display_reset(void) { +#ifndef EMULATOR + CLEAR_PIN(nRESET_PIN); + + delay_ms(10); + + SET_PIN(nRESET_PIN); + + delay_ms(10); +#endif +} + +/* + * display_reset_io() - reset display io port + * + * INPUT - none + * OUTPUT - none + */ +static void display_reset_io(void) { +#ifndef EMULATOR + + SET_PIN(nRESET_PIN); + SET_PIN(nDC_PIN); + SET_PIN(nSEL_PIN); + +#endif +} + +/* + * display_configure_io() - Setup display io port + * + * INPUT + * none + * OUTPUT + * none + */ +// static void display_configure_io(void) { +void display_configure_io(void) { +#ifndef EMULATOR + spi_setup(); + /* Set up port C OLED_RST=PC3, DC=PC2, CS=C1*/ + gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + GPIO1 | GPIO2 | GPIO3 | GPIO5); + + gpio_set_output_options(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, + GPIO1 | GPIO2 | GPIO3 | GPIO5); + + display_reset_io(); +#endif +} + +/* + * display_prepare_gram_write() - Prepare display for write + * + * INPUT + * none + * OUTPUT + * none + */ +static void display_prepare_gram_write(void) { +#ifndef EMULATOR + display_write_reg((uint8_t)0x5C); +#endif +} + +/* + * display_write_ram() - Write data to display + * + * INPUT + * - val: display ram value + * OUTPUT + * none + */ +static void display_write_ram(uint8_t val) { +#ifndef EMULATOR + // svc_disable_interrupts(); + + // /* Set nOLED_SEL low */ + CLEAR_PIN(nSEL_PIN); + + /* Set nDC high */ + SET_PIN(nDC_PIN); + + spi_send(SPI1, (uint16_t)val); + delay_us(10); + + /* Set nOLED_SEL high */ + SET_PIN(nSEL_PIN); + + // svc_enable_interrupts(); + +#endif +} + +/* + * display_canvas_init() - Display canvas initialization + * + * INPUT + * none + * OUTPUT + * pointer to canvas + */ +Canvas *display_canvas_init(void) { + /* Prepare the canvas */ + canvas.buffer = canvas_buffer; + canvas.width = KEEPKEY_DISPLAY_WIDTH; + canvas.height = KEEPKEY_DISPLAY_HEIGHT; + canvas.dirty = false; + + return &canvas; +} + +/* + * display_canvas() - Get pointer canvas + * + * INPUT + * none + * OUTPUT + * pointer to canvas + */ +Canvas *display_canvas(void) { return &canvas; } + +void (*DumpDisplay)(const uint8_t *buf) = 0; +void display_set_dump_callback(DumpDisplayCallback d) { DumpDisplay = d; } + +/* + * display_refresh() - Refresh display + * + * INPUT + * none + * OUTPUT + * none + */ +void display_refresh(void) +{ + if (DumpDisplay) { + DumpDisplay(canvas.buffer); + } + + if(!canvas.dirty) + { + return; + } + + if (constant_power) { + for (int y = 0; y < 64; y++) { + for (int x = 0; x < 128; x++) { + canvas.buffer[y * 256 + x] = 255 - canvas.buffer[y * 256 + x + 128]; + } + } + } + + display_prepare_gram_write(); + + int num_writes = canvas.width * canvas.height; + + int i; +#ifdef INVERT_DISPLAY + + for (i = num_writes; i > 0; i -= 2) { + uint8_t v = (0xF0 & canvas.buffer[i]) | (canvas.buffer[i - 1] >> 4); +#else + + for (i = 0; i < num_writes; i += 2) { + uint8_t v = (0xF0 & canvas.buffer[i]) | (canvas.buffer[i + 1] >> 4); +#endif + display_write_ram(v); + } + + canvas.dirty = false; +} + +/* + * display_turn_on() - Turn on display + * + * INPUT + * none + * OUTPUT + * none + */ +void display_turn_on(void) { display_write_reg((uint8_t)0xAF); } + +/* + * display_turn_off() - Turn off display + * + * INPUT + * none + * OUTPUT + * none + */ +void display_turn_off(void) +{ + display_write_reg((uint8_t)0xAE); +} + +void display_constant_power(bool enabled) +{ + constant_power = enabled; +} + +/* + * display_hw_init(void) - Display hardware initialization + * + * INPUT + * none + * OUTPUT + * none + */ +void display_hw_init(void) { +#ifndef EMULATOR + display_configure_io(); + display_reset(); + + display_write_reg((uint8_t)0xFD); + display_write_ram((uint8_t)0x12); + + display_turn_off(); + + /* Divide DIVSET by 2 */ + display_write_reg((uint8_t)0xB3); + display_write_ram((uint8_t)0x91); + + display_write_reg((uint8_t)0xCA); + display_write_ram((uint8_t)0x3F); + + display_write_reg((uint8_t)0xA2); + display_write_ram((uint8_t)0x00); + + display_write_reg((uint8_t)0xA1); + display_write_ram((uint8_t)0x00); + + uint8_t row_start = START_ROW; + uint8_t row_end = row_start + 64 - 1; + + /* Width is in units of 4 pixels/column (2 bytes at 4 bits/pixel) */ + int width = (256 / 4); + uint8_t col_start = START_COL; + uint8_t col_end = col_start + width - 1; + led_func(TGL_GREEN_LED); + + display_write_reg((uint8_t)0x75); + display_write_ram(row_start); + display_write_ram(row_end); + display_write_reg((uint8_t)0x15); + display_write_ram(col_start); + display_write_ram(col_end); + + /* Horizontal address increment */ + /* Disable colum address re-map */ + /* Disable nibble re-map */ + /* Scan from COM0 to COM[n-1] */ + /* Disable dual COM mode */ + display_write_reg((uint8_t)0xA0); + display_write_ram((uint8_t)0x14); + display_write_ram((uint8_t)0x11); + + /* GPIO0: pin HiZ, Input disabled */ + /* GPIO1: pin HiZ, Input disabled */ + display_write_reg((uint8_t)0xB5); + display_write_ram((uint8_t)0x00); + + /* Enable internal Vdd regulator */ + display_write_reg((uint8_t)0xAB); + display_write_ram((uint8_t)0x01); + + display_write_reg((uint8_t)0xB4); + display_write_ram((uint8_t)0xA0); + display_write_ram((uint8_t)0xFD); + + display_set_brightness(DEFAULT_DISPLAY_BRIGHTNESS); + + display_write_reg((uint8_t)0xC7); + display_write_ram((uint8_t)0x0F); + + display_write_reg((uint8_t)0xB9); + + display_write_reg((uint8_t)0xB1); + display_write_ram((uint8_t)0xE2); + + display_write_reg((uint8_t)0xD1); + display_write_ram((uint8_t)0x82); + display_write_ram((uint8_t)0x20); + + display_write_reg((uint8_t)0xBB); + display_write_ram((uint8_t)0x1F); + + display_write_reg((uint8_t)0xB6); + display_write_ram((uint8_t)0x08); + + display_write_reg((uint8_t)0xBE); + display_write_ram((uint8_t)0x07); + + display_write_reg((uint8_t)0xA6); + + delay_ms(10); + + /* Set the screen to display-writing mode */ + display_prepare_gram_write(); + + delay_ms(10); + + /* Make the display blank */ + int end = 64 * 256; + int i; + + for (i = 0; i < end; i += 2) { + display_write_ram((uint8_t)0x00); + } + + /* Turn on 12V */ + // SET_PIN(BACKLIGHT_PWR_PIN); + + delay_ms(100); + + display_turn_on(); +#endif +} + +/* + * display_set_brightness() - Set display brightness in percentage + * + * INPUT + * - percentage: brightness percentage + * OUTPUT + * none + */ +void display_set_brightness(int percentage) { +#ifndef EMULATOR + int v = percentage; + + /* Clip to be 0 <= value <= 100 */ + v = (v >= 0) ? v : 0; + v = (v > 100) ? 100 : v; + + v = (0xFF * v) / 100; + + uint8_t reg_value = (uint8_t)v; + + display_write_reg((uint8_t)0xC1); + display_write_ram(reg_value); +#endif +} + +#pragma GCC pop_options diff --git a/lib/board/memcmp_s.c b/lib/board/memcmp_s.c index 336762383..9f5cb3b13 100644 --- a/lib/board/memcmp_s.c +++ b/lib/board/memcmp_s.c @@ -1,8 +1,8 @@ #include "keepkey/board/memcmp_s.h" #include "keepkey/rand/rng.h" -#include "trezor/crypto/rand.h" -#include "trezor/crypto/memzero.h" +#include "hwcrypto/crypto/rand.h" +#include "hwcrypto/crypto/memzero.h" #include #include diff --git a/lib/board/memory.c b/lib/board/memory.c index 7fe46b310..dba870389 100644 --- a/lib/board/memory.c +++ b/lib/board/memory.c @@ -27,8 +27,8 @@ #include #endif -#include "trezor/crypto/sha2.h" -#include "trezor/crypto/sha3.h" +#include "hwcrypto/crypto/sha2.h" +#include "hwcrypto/crypto/sha3.h" #include "keepkey/board/confirm_sm.h" #include "keepkey/board/keepkey_board.h" @@ -110,6 +110,15 @@ void mpu_config(int priv_level) { MPU_RBAR = 0x40004800 | MPU_RBAR_VALID | (5 << MPU_RBAR_REGION_LSB); MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_1KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + +#elif defined DEV_DEBUG + // unpriv access to SPI1 for development debugging +// USART3 is open to unprivileged access for usart debug versions only + // (0x40013000 - 0x400133ff) + MPU_RBAR = 0x40013000 | MPU_RBAR_VALID | (5 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_1KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + #else // If using release firmware, use this region to protect the sysconfig // registers (0x40013800 - 0x40013BFF, read-only, execute never) diff --git a/lib/board/messages.c b/lib/board/messages.c index 16da635d1..33648e082 100644 --- a/lib/board/messages.c +++ b/lib/board/messages.c @@ -76,7 +76,7 @@ static const MessagesMap_t *message_map_entry(MessageMapType type, * OUTPUT * protocol buffer */ -const pb_field_t *message_fields(MessageMapType type, MessageType msg_id, +const pb_msgdesc_t *message_fields(MessageMapType type, MessageType msg_id, MessageMapDirection dir) { assert(MessagesMap != NULL); @@ -341,7 +341,7 @@ static void msg_read_tiny(const uint8_t *msg, size_t len) { return; } - const pb_field_t *fields = NULL; + const pb_msgdesc_t *fields = NULL; pb_istream_t stream = pb_istream_from_buffer(buf + 9, msgSize); switch (msgId) { @@ -595,9 +595,9 @@ uint32_t parse_pb_varint(RawMessage *msg, uint8_t varint_count) { * OUTPUT * bytes written to buffer */ -int encode_pb(const void *source_ptr, const pb_field_t *fields, uint8_t *buffer, +int encode_pb(const void *source_ptr, const pb_msgdesc_t *fields, uint8_t *buffer, uint32_t len) { - pb_ostream_t os = pb_ostream_from_buffer(buffer, len); +pb_ostream_t os = pb_ostream_from_buffer(buffer, len); if (!pb_encode(&os, fields, source_ptr)) return 0; diff --git a/lib/board/mmhusr.c b/lib/board/mmhusr.c index 794074d92..8abe22a91 100644 --- a/lib/board/mmhusr.c +++ b/lib/board/mmhusr.c @@ -20,8 +20,12 @@ #ifndef EMULATOR #include #include -#include +#ifdef DEV_DEBUG +#include #else +#include +#endif +#else // EMULATOR #include #include #include diff --git a/lib/board/pin.c b/lib/board/pin.c index e6cd0776b..201a7c0e6 100644 --- a/lib/board/pin.c +++ b/lib/board/pin.c @@ -64,7 +64,7 @@ void pin_init_output(const Pin *pin, OutputMode output_mode, break; } - /* Set up port A */ + /* Set pin parameters */ gpio_mode_setup(pin->port, GPIO_MODE_OUTPUT, pull_mode_setpoint, pin->pin); gpio_set_output_options(pin->port, output_mode_setpoint, GPIO_OSPEED_100MHZ, pin->pin); diff --git a/lib/board/resources.c b/lib/board/resources.c index 2a4f46a4b..b8b451406 100644 --- a/lib/board/resources.c +++ b/lib/board/resources.c @@ -21,6 +21,11 @@ #include +#ifdef DEV_DEBUG +#define ATIME 2 // the spi display and clock diffs require a shorter animation time on the dev board +#else +#define ATIME 20 +#endif /* --- Confirm Icon Animation ---------------------------------------------- */ static const uint8_t confirm_icon_data[240] = { @@ -46,7 +51,9 @@ static const uint8_t confirm_icon_data[240] = { 0x55, 0xa3, 0xd5, 0x02, 0xf1, 0xfc, 0xd5, 0xa3, 0x55, 0x0a, 0x08, 0x00}; static const Image confirm_icon_image = {22, 18, sizeof(confirm_icon_data), confirm_icon_data}; -const AnimationFrame confirm_icon_frame = {233, 4, 20, 100, +// const AnimationFrame confirm_icon_frame = {233, 4, 20, 100, +// &confirm_icon_image}; +const AnimationFrame confirm_icon_frame = {233, 4, 30, 100, &confirm_icon_image}; /* --- Confirming Animation ------------------------------------------------ */ @@ -1656,70 +1663,70 @@ static const Image confirming_64_image = {22, 22, sizeof(confirming_64_data), const VariantAnimation confirming = { 64, { - {231, 2, 20, 100, &confirming_1_image}, - {231, 2, 20, 100, &confirming_2_image}, - {231, 2, 20, 100, &confirming_3_image}, - {231, 2, 20, 100, &confirming_4_image}, - {231, 2, 20, 100, &confirming_5_image}, - {231, 2, 20, 100, &confirming_6_image}, - {231, 2, 20, 100, &confirming_7_image}, - {231, 2, 20, 100, &confirming_8_image}, - {231, 2, 20, 100, &confirming_9_image}, - {231, 2, 20, 100, &confirming_10_image}, - {231, 2, 20, 100, &confirming_11_image}, - {231, 2, 20, 100, &confirming_12_image}, - {231, 2, 20, 100, &confirming_13_image}, - {231, 2, 20, 100, &confirming_14_image}, - {231, 2, 20, 100, &confirming_15_image}, - {231, 2, 20, 100, &confirming_16_image}, - {231, 2, 20, 100, &confirming_17_image}, - {231, 2, 20, 100, &confirming_18_image}, - {231, 2, 20, 100, &confirming_19_image}, - {231, 2, 20, 100, &confirming_20_image}, - {231, 2, 20, 100, &confirming_21_image}, - {231, 2, 20, 100, &confirming_22_image}, - {231, 2, 20, 100, &confirming_23_image}, - {231, 2, 20, 100, &confirming_24_image}, - {231, 2, 20, 100, &confirming_25_image}, - {231, 2, 20, 100, &confirming_26_image}, - {231, 2, 20, 100, &confirming_27_image}, - {231, 2, 20, 100, &confirming_28_image}, - {231, 2, 20, 100, &confirming_29_image}, - {231, 2, 20, 100, &confirming_30_image}, - {231, 2, 20, 100, &confirming_31_image}, - {231, 2, 20, 100, &confirming_32_image}, - {231, 2, 20, 100, &confirming_33_image}, - {231, 2, 20, 100, &confirming_34_image}, - {231, 2, 20, 100, &confirming_35_image}, - {231, 2, 20, 100, &confirming_36_image}, - {231, 2, 20, 100, &confirming_37_image}, - {231, 2, 20, 100, &confirming_38_image}, - {231, 2, 20, 100, &confirming_39_image}, - {231, 2, 20, 100, &confirming_40_image}, - {231, 2, 20, 100, &confirming_41_image}, - {231, 2, 20, 100, &confirming_42_image}, - {231, 2, 20, 100, &confirming_43_image}, - {231, 2, 20, 100, &confirming_44_image}, - {231, 2, 20, 100, &confirming_45_image}, - {231, 2, 20, 100, &confirming_46_image}, - {231, 2, 20, 100, &confirming_47_image}, - {231, 2, 20, 100, &confirming_48_image}, - {231, 2, 20, 100, &confirming_49_image}, - {231, 2, 20, 100, &confirming_50_image}, - {231, 2, 20, 100, &confirming_51_image}, - {231, 2, 20, 100, &confirming_52_image}, - {231, 2, 20, 100, &confirming_53_image}, - {231, 2, 20, 100, &confirming_54_image}, - {231, 2, 20, 100, &confirming_55_image}, - {231, 2, 20, 100, &confirming_56_image}, - {231, 2, 20, 100, &confirming_57_image}, - {231, 2, 20, 100, &confirming_58_image}, - {231, 2, 20, 100, &confirming_59_image}, - {231, 2, 20, 100, &confirming_60_image}, - {231, 2, 20, 100, &confirming_61_image}, - {231, 2, 20, 100, &confirming_62_image}, - {231, 2, 20, 100, &confirming_63_image}, - {231, 2, 20, 100, &confirming_64_image}, + {231, 2, ATIME, 100, &confirming_1_image}, + {231, 2, ATIME, 100, &confirming_2_image}, + {231, 2, ATIME, 100, &confirming_3_image}, + {231, 2, ATIME, 100, &confirming_4_image}, + {231, 2, ATIME, 100, &confirming_5_image}, + {231, 2, ATIME, 100, &confirming_6_image}, + {231, 2, ATIME, 100, &confirming_7_image}, + {231, 2, ATIME, 100, &confirming_8_image}, + {231, 2, ATIME, 100, &confirming_9_image}, + {231, 2, ATIME, 100, &confirming_10_image}, + {231, 2, ATIME, 100, &confirming_11_image}, + {231, 2, ATIME, 100, &confirming_12_image}, + {231, 2, ATIME, 100, &confirming_13_image}, + {231, 2, ATIME, 100, &confirming_14_image}, + {231, 2, ATIME, 100, &confirming_15_image}, + {231, 2, ATIME, 100, &confirming_16_image}, + {231, 2, ATIME, 100, &confirming_17_image}, + {231, 2, ATIME, 100, &confirming_18_image}, + {231, 2, ATIME, 100, &confirming_19_image}, + {231, 2, ATIME, 100, &confirming_20_image}, + {231, 2, ATIME, 100, &confirming_21_image}, + {231, 2, ATIME, 100, &confirming_22_image}, + {231, 2, ATIME, 100, &confirming_23_image}, + {231, 2, ATIME, 100, &confirming_24_image}, + {231, 2, ATIME, 100, &confirming_25_image}, + {231, 2, ATIME, 100, &confirming_26_image}, + {231, 2, ATIME, 100, &confirming_27_image}, + {231, 2, ATIME, 100, &confirming_28_image}, + {231, 2, ATIME, 100, &confirming_29_image}, + {231, 2, ATIME, 100, &confirming_30_image}, + {231, 2, ATIME, 100, &confirming_31_image}, + {231, 2, ATIME, 100, &confirming_32_image}, + {231, 2, ATIME, 100, &confirming_33_image}, + {231, 2, ATIME, 100, &confirming_34_image}, + {231, 2, ATIME, 100, &confirming_35_image}, + {231, 2, ATIME, 100, &confirming_36_image}, + {231, 2, ATIME, 100, &confirming_37_image}, + {231, 2, ATIME, 100, &confirming_38_image}, + {231, 2, ATIME, 100, &confirming_39_image}, + {231, 2, ATIME, 100, &confirming_40_image}, + {231, 2, ATIME, 100, &confirming_41_image}, + {231, 2, ATIME, 100, &confirming_42_image}, + {231, 2, ATIME, 100, &confirming_43_image}, + {231, 2, ATIME, 100, &confirming_44_image}, + {231, 2, ATIME, 100, &confirming_45_image}, + {231, 2, ATIME, 100, &confirming_46_image}, + {231, 2, ATIME, 100, &confirming_47_image}, + {231, 2, ATIME, 100, &confirming_48_image}, + {231, 2, ATIME, 100, &confirming_49_image}, + {231, 2, ATIME, 100, &confirming_50_image}, + {231, 2, ATIME, 100, &confirming_51_image}, + {231, 2, ATIME, 100, &confirming_52_image}, + {231, 2, ATIME, 100, &confirming_53_image}, + {231, 2, ATIME, 100, &confirming_54_image}, + {231, 2, ATIME, 100, &confirming_55_image}, + {231, 2, ATIME, 100, &confirming_56_image}, + {231, 2, ATIME, 100, &confirming_57_image}, + {231, 2, ATIME, 100, &confirming_58_image}, + {231, 2, ATIME, 100, &confirming_59_image}, + {231, 2, ATIME, 100, &confirming_60_image}, + {231, 2, ATIME, 100, &confirming_61_image}, + {231, 2, ATIME, 100, &confirming_62_image}, + {231, 2, ATIME, 100, &confirming_63_image}, + {231, 2, ATIME, 100, &confirming_64_image}, }}; /* --- Warning Animation --------------------------------------------------- */ diff --git a/lib/board/signatures.c b/lib/board/signatures.c index c6b88026c..ede1665b3 100644 --- a/lib/board/signatures.c +++ b/lib/board/signatures.c @@ -17,9 +17,9 @@ * along with this library. If not, see . */ -#include "trezor/crypto/sha2.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/secp256k1.h" +#include "hwcrypto/crypto/sha2.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/secp256k1.h" #include "keepkey/board/memory.h" #include "keepkey/board/signatures.h" #include "keepkey/board/pubkeys.h" diff --git a/lib/board/ssd1351/fonts.c b/lib/board/ssd1351/fonts.c new file mode 100644 index 000000000..ccbaccb02 --- /dev/null +++ b/lib/board/ssd1351/fonts.c @@ -0,0 +1,301 @@ + +#include "keepkey/board/ssd1351/fonts.h" + +static const uint16_t Font7x10 [] = { +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // sp +0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x0000, 0x0000, // ! +0x2800, 0x2800, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // " +0x2400, 0x2400, 0x7C00, 0x2400, 0x4800, 0x7C00, 0x4800, 0x4800, 0x0000, 0x0000, // # +0x3800, 0x5400, 0x5000, 0x3800, 0x1400, 0x5400, 0x5400, 0x3800, 0x1000, 0x0000, // $ +0x2000, 0x5400, 0x5800, 0x3000, 0x2800, 0x5400, 0x1400, 0x0800, 0x0000, 0x0000, // % +0x1000, 0x2800, 0x2800, 0x1000, 0x3400, 0x4800, 0x4800, 0x3400, 0x0000, 0x0000, // & +0x1000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ' +0x0800, 0x1000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x1000, 0x0800, // ( +0x2000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x1000, 0x2000, // ) +0x1000, 0x3800, 0x1000, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // * +0x0000, 0x0000, 0x1000, 0x1000, 0x7C00, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, // + +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x1000, // , +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, // - +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, // . +0x0800, 0x0800, 0x1000, 0x1000, 0x1000, 0x1000, 0x2000, 0x2000, 0x0000, 0x0000, // / +0x3800, 0x4400, 0x4400, 0x5400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 0 +0x1000, 0x3000, 0x5000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // 1 +0x3800, 0x4400, 0x4400, 0x0400, 0x0800, 0x1000, 0x2000, 0x7C00, 0x0000, 0x0000, // 2 +0x3800, 0x4400, 0x0400, 0x1800, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 3 +0x0800, 0x1800, 0x2800, 0x2800, 0x4800, 0x7C00, 0x0800, 0x0800, 0x0000, 0x0000, // 4 +0x7C00, 0x4000, 0x4000, 0x7800, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 5 +0x3800, 0x4400, 0x4000, 0x7800, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 6 +0x7C00, 0x0400, 0x0800, 0x1000, 0x1000, 0x2000, 0x2000, 0x2000, 0x0000, 0x0000, // 7 +0x3800, 0x4400, 0x4400, 0x3800, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 8 +0x3800, 0x4400, 0x4400, 0x4400, 0x3C00, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 9 +0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, // : +0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x1000, // ; +0x0000, 0x0000, 0x0C00, 0x3000, 0x4000, 0x3000, 0x0C00, 0x0000, 0x0000, 0x0000, // < +0x0000, 0x0000, 0x0000, 0x7C00, 0x0000, 0x7C00, 0x0000, 0x0000, 0x0000, 0x0000, // = +0x0000, 0x0000, 0x6000, 0x1800, 0x0400, 0x1800, 0x6000, 0x0000, 0x0000, 0x0000, // > +0x3800, 0x4400, 0x0400, 0x0800, 0x1000, 0x1000, 0x0000, 0x1000, 0x0000, 0x0000, // ? +0x3800, 0x4400, 0x4C00, 0x5400, 0x5C00, 0x4000, 0x4000, 0x3800, 0x0000, 0x0000, // @ +0x1000, 0x2800, 0x2800, 0x2800, 0x2800, 0x7C00, 0x4400, 0x4400, 0x0000, 0x0000, // A +0x7800, 0x4400, 0x4400, 0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x0000, 0x0000, // B +0x3800, 0x4400, 0x4000, 0x4000, 0x4000, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // C +0x7000, 0x4800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4800, 0x7000, 0x0000, 0x0000, // D +0x7C00, 0x4000, 0x4000, 0x7C00, 0x4000, 0x4000, 0x4000, 0x7C00, 0x0000, 0x0000, // E +0x7C00, 0x4000, 0x4000, 0x7800, 0x4000, 0x4000, 0x4000, 0x4000, 0x0000, 0x0000, // F +0x3800, 0x4400, 0x4000, 0x4000, 0x5C00, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // G +0x4400, 0x4400, 0x4400, 0x7C00, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // H +0x3800, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x3800, 0x0000, 0x0000, // I +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // J +0x4400, 0x4800, 0x5000, 0x6000, 0x5000, 0x4800, 0x4800, 0x4400, 0x0000, 0x0000, // K +0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x7C00, 0x0000, 0x0000, // L +0x4400, 0x6C00, 0x6C00, 0x5400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // M +0x4400, 0x6400, 0x6400, 0x5400, 0x5400, 0x4C00, 0x4C00, 0x4400, 0x0000, 0x0000, // N +0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // O +0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x4000, 0x4000, 0x4000, 0x0000, 0x0000, // P +0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x5400, 0x3800, 0x0400, 0x0000, // Q +0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x4800, 0x4800, 0x4400, 0x0000, 0x0000, // R +0x3800, 0x4400, 0x4000, 0x3000, 0x0800, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // S +0x7C00, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // T +0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // U +0x4400, 0x4400, 0x4400, 0x2800, 0x2800, 0x2800, 0x1000, 0x1000, 0x0000, 0x0000, // V +0x4400, 0x4400, 0x5400, 0x5400, 0x5400, 0x6C00, 0x2800, 0x2800, 0x0000, 0x0000, // W +0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x2800, 0x2800, 0x4400, 0x0000, 0x0000, // X +0x4400, 0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // Y +0x7C00, 0x0400, 0x0800, 0x1000, 0x1000, 0x2000, 0x4000, 0x7C00, 0x0000, 0x0000, // Z +0x1800, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1800, // [ +0x2000, 0x2000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0000, 0x0000, /* \ */ +0x3000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x3000, // ] +0x1000, 0x2800, 0x2800, 0x4400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ^ +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFE00, // _ +0x2000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ` +0x0000, 0x0000, 0x3800, 0x4400, 0x3C00, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // a +0x4000, 0x4000, 0x5800, 0x6400, 0x4400, 0x4400, 0x6400, 0x5800, 0x0000, 0x0000, // b +0x0000, 0x0000, 0x3800, 0x4400, 0x4000, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // c +0x0400, 0x0400, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // d +0x0000, 0x0000, 0x3800, 0x4400, 0x7C00, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // e +0x0C00, 0x1000, 0x7C00, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // f +0x0000, 0x0000, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0400, 0x7800, // g +0x4000, 0x4000, 0x5800, 0x6400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // h +0x1000, 0x0000, 0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // i +0x1000, 0x0000, 0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0xE000, // j +0x4000, 0x4000, 0x4800, 0x5000, 0x6000, 0x5000, 0x4800, 0x4400, 0x0000, 0x0000, // k +0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // l +0x0000, 0x0000, 0x7800, 0x5400, 0x5400, 0x5400, 0x5400, 0x5400, 0x0000, 0x0000, // m +0x0000, 0x0000, 0x5800, 0x6400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // n +0x0000, 0x0000, 0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // o +0x0000, 0x0000, 0x5800, 0x6400, 0x4400, 0x4400, 0x6400, 0x5800, 0x4000, 0x4000, // p +0x0000, 0x0000, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0400, 0x0400, // q +0x0000, 0x0000, 0x5800, 0x6400, 0x4000, 0x4000, 0x4000, 0x4000, 0x0000, 0x0000, // r +0x0000, 0x0000, 0x3800, 0x4400, 0x3000, 0x0800, 0x4400, 0x3800, 0x0000, 0x0000, // s +0x2000, 0x2000, 0x7800, 0x2000, 0x2000, 0x2000, 0x2000, 0x1800, 0x0000, 0x0000, // t +0x0000, 0x0000, 0x4400, 0x4400, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // u +0x0000, 0x0000, 0x4400, 0x4400, 0x2800, 0x2800, 0x2800, 0x1000, 0x0000, 0x0000, // v +0x0000, 0x0000, 0x5400, 0x5400, 0x5400, 0x6C00, 0x2800, 0x2800, 0x0000, 0x0000, // w +0x0000, 0x0000, 0x4400, 0x2800, 0x1000, 0x1000, 0x2800, 0x4400, 0x0000, 0x0000, // x +0x0000, 0x0000, 0x4400, 0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x1000, 0x6000, // y +0x0000, 0x0000, 0x7C00, 0x0800, 0x1000, 0x2000, 0x4000, 0x7C00, 0x0000, 0x0000, // z +0x1800, 0x1000, 0x1000, 0x1000, 0x2000, 0x2000, 0x1000, 0x1000, 0x1000, 0x1800, // { +0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, // | +0x3000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x1000, 0x1000, 0x1000, 0x3000, // } +0x0000, 0x0000, 0x0000, 0x7400, 0x4C00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ~ +}; + +static const uint16_t Font11x18 [] = { +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // sp +0x0000, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0000, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, // ! +0x0000, 0x1B00, 0x1B00, 0x1B00, 0x1B00, 0x1B00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // " +0x0000, 0x1980, 0x1980, 0x1980, 0x1980, 0x7FC0, 0x7FC0, 0x1980, 0x3300, 0x7FC0, 0x7FC0, 0x3300, 0x3300, 0x3300, 0x3300, 0x0000, 0x0000, 0x0000, // # +0x0000, 0x1E00, 0x3F00, 0x7580, 0x6580, 0x7400, 0x3C00, 0x1E00, 0x0700, 0x0580, 0x6580, 0x6580, 0x7580, 0x3F00, 0x1E00, 0x0400, 0x0400, 0x0000, // $ +0x0000, 0x7000, 0xD800, 0xD840, 0xD8C0, 0xD980, 0x7300, 0x0600, 0x0C00, 0x1B80, 0x36C0, 0x66C0, 0x46C0, 0x06C0, 0x0380, 0x0000, 0x0000, 0x0000, // % +0x0000, 0x1E00, 0x3F00, 0x3300, 0x3300, 0x3300, 0x1E00, 0x0C00, 0x3CC0, 0x66C0, 0x6380, 0x6180, 0x6380, 0x3EC0, 0x1C80, 0x0000, 0x0000, 0x0000, // & +0x0000, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ' +0x0080, 0x0100, 0x0300, 0x0600, 0x0600, 0x0400, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0400, 0x0600, 0x0600, 0x0300, 0x0100, 0x0080, // ( +0x2000, 0x1000, 0x1800, 0x0C00, 0x0C00, 0x0400, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0400, 0x0C00, 0x0C00, 0x1800, 0x1000, 0x2000, // ) +0x0000, 0x0C00, 0x2D00, 0x3F00, 0x1E00, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // * +0x0000, 0x0000, 0x0000, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0xFFC0, 0xFFC0, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // + +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0C00, 0x0C00, 0x0400, 0x0400, 0x0800, // , +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E00, 0x1E00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // - +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, // . +0x0000, 0x0300, 0x0300, 0x0300, 0x0600, 0x0600, 0x0600, 0x0600, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x1800, 0x1800, 0x1800, 0x0000, 0x0000, 0x0000, // / +0x0000, 0x1E00, 0x3F00, 0x3300, 0x6180, 0x6180, 0x6180, 0x6D80, 0x6D80, 0x6180, 0x6180, 0x6180, 0x3300, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // 0 +0x0000, 0x0600, 0x0E00, 0x1E00, 0x3600, 0x2600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, // 1 +0x0000, 0x1E00, 0x3F00, 0x7380, 0x6180, 0x6180, 0x0180, 0x0300, 0x0600, 0x0C00, 0x1800, 0x3000, 0x6000, 0x7F80, 0x7F80, 0x0000, 0x0000, 0x0000, // 2 +0x0000, 0x1C00, 0x3E00, 0x6300, 0x6300, 0x0300, 0x0E00, 0x0E00, 0x0300, 0x0180, 0x0180, 0x6180, 0x7380, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // 3 +0x0000, 0x0600, 0x0E00, 0x0E00, 0x1E00, 0x1E00, 0x1600, 0x3600, 0x3600, 0x6600, 0x7F80, 0x7F80, 0x0600, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, // 4 +0x0000, 0x7F00, 0x7F00, 0x6000, 0x6000, 0x6000, 0x6E00, 0x7F00, 0x6380, 0x0180, 0x0180, 0x6180, 0x7380, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // 5 +0x0000, 0x1E00, 0x3F00, 0x3380, 0x6180, 0x6000, 0x6E00, 0x7F00, 0x7380, 0x6180, 0x6180, 0x6180, 0x3380, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // 6 +0x0000, 0x7F80, 0x7F80, 0x0180, 0x0300, 0x0300, 0x0600, 0x0600, 0x0C00, 0x0C00, 0x0C00, 0x0800, 0x1800, 0x1800, 0x1800, 0x0000, 0x0000, 0x0000, // 7 +0x0000, 0x1E00, 0x3F00, 0x6380, 0x6180, 0x6180, 0x2100, 0x1E00, 0x3F00, 0x6180, 0x6180, 0x6180, 0x6180, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // 8 +0x0000, 0x1E00, 0x3F00, 0x7300, 0x6180, 0x6180, 0x6180, 0x7380, 0x3F80, 0x1D80, 0x0180, 0x6180, 0x7300, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // 9 +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, // : +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0C00, 0x0C00, 0x0400, 0x0400, 0x0800, // ; +0x0000, 0x0000, 0x0000, 0x0000, 0x0080, 0x0380, 0x0E00, 0x3800, 0x6000, 0x3800, 0x0E00, 0x0380, 0x0080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // < +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7F80, 0x7F80, 0x0000, 0x0000, 0x7F80, 0x7F80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // = +0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x7000, 0x1C00, 0x0700, 0x0180, 0x0700, 0x1C00, 0x7000, 0x4000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // > +0x0000, 0x1F00, 0x3F80, 0x71C0, 0x60C0, 0x00C0, 0x01C0, 0x0380, 0x0700, 0x0E00, 0x0C00, 0x0C00, 0x0000, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, // ? +0x0000, 0x1E00, 0x3F00, 0x3180, 0x7180, 0x6380, 0x6F80, 0x6D80, 0x6D80, 0x6F80, 0x6780, 0x6000, 0x3200, 0x3E00, 0x1C00, 0x0000, 0x0000, 0x0000, // @ +0x0000, 0x0E00, 0x0E00, 0x1B00, 0x1B00, 0x1B00, 0x1B00, 0x3180, 0x3180, 0x3F80, 0x3F80, 0x3180, 0x60C0, 0x60C0, 0x60C0, 0x0000, 0x0000, 0x0000, // A +0x0000, 0x7C00, 0x7E00, 0x6300, 0x6300, 0x6300, 0x6300, 0x7E00, 0x7E00, 0x6300, 0x6180, 0x6180, 0x6380, 0x7F00, 0x7E00, 0x0000, 0x0000, 0x0000, // B +0x0000, 0x1E00, 0x3F00, 0x3180, 0x6180, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6180, 0x3180, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // C +0x0000, 0x7C00, 0x7F00, 0x6300, 0x6380, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6300, 0x6300, 0x7E00, 0x7C00, 0x0000, 0x0000, 0x0000, // D +0x0000, 0x7F80, 0x7F80, 0x6000, 0x6000, 0x6000, 0x6000, 0x7F00, 0x7F00, 0x6000, 0x6000, 0x6000, 0x6000, 0x7F80, 0x7F80, 0x0000, 0x0000, 0x0000, // E +0x0000, 0x7F80, 0x7F80, 0x6000, 0x6000, 0x6000, 0x6000, 0x7F00, 0x7F00, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x0000, 0x0000, 0x0000, // F +0x0000, 0x1E00, 0x3F00, 0x3180, 0x6180, 0x6000, 0x6000, 0x6000, 0x6380, 0x6380, 0x6180, 0x6180, 0x3180, 0x3F80, 0x1E00, 0x0000, 0x0000, 0x0000, // G +0x0000, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x7F80, 0x7F80, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x0000, 0x0000, 0x0000, // H +0x0000, 0x3F00, 0x3F00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x3F00, 0x3F00, 0x0000, 0x0000, 0x0000, // I +0x0000, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x6180, 0x6180, 0x7380, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // J +0x0000, 0x60C0, 0x6180, 0x6300, 0x6600, 0x6600, 0x6C00, 0x7800, 0x7C00, 0x6600, 0x6600, 0x6300, 0x6180, 0x6180, 0x60C0, 0x0000, 0x0000, 0x0000, // K +0x0000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x7F80, 0x7F80, 0x0000, 0x0000, 0x0000, // L +0x0000, 0x71C0, 0x71C0, 0x7BC0, 0x7AC0, 0x6AC0, 0x6AC0, 0x6EC0, 0x64C0, 0x60C0, 0x60C0, 0x60C0, 0x60C0, 0x60C0, 0x60C0, 0x0000, 0x0000, 0x0000, // M +0x0000, 0x7180, 0x7180, 0x7980, 0x7980, 0x7980, 0x6D80, 0x6D80, 0x6D80, 0x6580, 0x6780, 0x6780, 0x6780, 0x6380, 0x6380, 0x0000, 0x0000, 0x0000, // N +0x0000, 0x1E00, 0x3F00, 0x3300, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x3300, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // O +0x0000, 0x7E00, 0x7F00, 0x6380, 0x6180, 0x6180, 0x6180, 0x6380, 0x7F00, 0x7E00, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x0000, 0x0000, 0x0000, // P +0x0000, 0x1E00, 0x3F00, 0x3300, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6580, 0x6780, 0x3300, 0x3F80, 0x1E40, 0x0000, 0x0000, 0x0000, // Q +0x0000, 0x7E00, 0x7F00, 0x6380, 0x6180, 0x6180, 0x6380, 0x7F00, 0x7E00, 0x6600, 0x6300, 0x6300, 0x6180, 0x6180, 0x60C0, 0x0000, 0x0000, 0x0000, // R +0x0000, 0x0E00, 0x1F00, 0x3180, 0x3180, 0x3000, 0x3800, 0x1E00, 0x0700, 0x0380, 0x6180, 0x6180, 0x3180, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // S +0x0000, 0xFFC0, 0xFFC0, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, // T +0x0000, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x7380, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // U +0x0000, 0x60C0, 0x60C0, 0x60C0, 0x3180, 0x3180, 0x3180, 0x1B00, 0x1B00, 0x1B00, 0x1B00, 0x0E00, 0x0E00, 0x0E00, 0x0400, 0x0000, 0x0000, 0x0000, // V +0x0000, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xCCC0, 0x4C80, 0x4C80, 0x5E80, 0x5280, 0x5280, 0x7380, 0x6180, 0x6180, 0x0000, 0x0000, 0x0000, // W +0x0000, 0xC0C0, 0x6080, 0x6180, 0x3300, 0x3B00, 0x1E00, 0x0C00, 0x0C00, 0x1E00, 0x1F00, 0x3B00, 0x7180, 0x6180, 0xC0C0, 0x0000, 0x0000, 0x0000, // X +0x0000, 0xC0C0, 0x6180, 0x6180, 0x3300, 0x3300, 0x1E00, 0x1E00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, // Y +0x0000, 0x3F80, 0x3F80, 0x0180, 0x0300, 0x0300, 0x0600, 0x0C00, 0x0C00, 0x1800, 0x1800, 0x3000, 0x6000, 0x7F80, 0x7F80, 0x0000, 0x0000, 0x0000, // Z +0x0F00, 0x0F00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0F00, 0x0F00, // [ +0x0000, 0x1800, 0x1800, 0x1800, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0600, 0x0600, 0x0600, 0x0600, 0x0300, 0x0300, 0x0300, 0x0000, 0x0000, 0x0000, /* \ */ +0x1E00, 0x1E00, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x1E00, 0x1E00, // ] +0x0000, 0x0C00, 0x0C00, 0x1E00, 0x1200, 0x3300, 0x3300, 0x6180, 0x6180, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ^ +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFE0, 0x0000, // _ +0x0000, 0x3800, 0x1800, 0x0C00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ` +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1F00, 0x3F80, 0x6180, 0x0180, 0x1F80, 0x3F80, 0x6180, 0x6380, 0x7F80, 0x38C0, 0x0000, 0x0000, 0x0000, // a +0x0000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6E00, 0x7F00, 0x7380, 0x6180, 0x6180, 0x6180, 0x6180, 0x7380, 0x7F00, 0x6E00, 0x0000, 0x0000, 0x0000, // b +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E00, 0x3F00, 0x7380, 0x6180, 0x6000, 0x6000, 0x6180, 0x7380, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // c +0x0000, 0x0180, 0x0180, 0x0180, 0x0180, 0x1D80, 0x3F80, 0x7380, 0x6180, 0x6180, 0x6180, 0x6180, 0x7380, 0x3F80, 0x1D80, 0x0000, 0x0000, 0x0000, // d +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E00, 0x3F00, 0x7300, 0x6180, 0x7F80, 0x7F80, 0x6000, 0x7180, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // e +0x0000, 0x07C0, 0x0FC0, 0x0C00, 0x0C00, 0x7F80, 0x7F80, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0000, 0x0000, 0x0000, // f +0x0000, 0x0000, 0x0000, 0x0000, 0x1D80, 0x3F80, 0x7380, 0x6180, 0x6180, 0x6180, 0x6180, 0x7380, 0x3F80, 0x1D80, 0x0180, 0x6380, 0x7F00, 0x3E00, // g +0x0000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6F00, 0x7F80, 0x7180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x0000, 0x0000, 0x0000, // h +0x0000, 0x0600, 0x0600, 0x0000, 0x0000, 0x3E00, 0x3E00, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, // i +0x0600, 0x0600, 0x0000, 0x0000, 0x3E00, 0x3E00, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x4600, 0x7E00, 0x3C00, // j +0x0000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6180, 0x6300, 0x6600, 0x6C00, 0x7C00, 0x7600, 0x6300, 0x6300, 0x6180, 0x60C0, 0x0000, 0x0000, 0x0000, // k +0x0000, 0x3E00, 0x3E00, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, // l +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xDD80, 0xFFC0, 0xCEC0, 0xCCC0, 0xCCC0, 0xCCC0, 0xCCC0, 0xCCC0, 0xCCC0, 0xCCC0, 0x0000, 0x0000, 0x0000, // m +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6F00, 0x7F80, 0x7180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x0000, 0x0000, 0x0000, // n +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E00, 0x3F00, 0x7380, 0x6180, 0x6180, 0x6180, 0x6180, 0x7380, 0x3F00, 0x1E00, 0x0000, 0x0000, 0x0000, // o +0x0000, 0x0000, 0x0000, 0x0000, 0x6E00, 0x7F00, 0x7380, 0x6180, 0x6180, 0x6180, 0x6180, 0x7380, 0x7F00, 0x6E00, 0x6000, 0x6000, 0x6000, 0x6000, // p +0x0000, 0x0000, 0x0000, 0x0000, 0x1D80, 0x3F80, 0x7380, 0x6180, 0x6180, 0x6180, 0x6180, 0x7380, 0x3F80, 0x1D80, 0x0180, 0x0180, 0x0180, 0x0180, // q +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6700, 0x3F80, 0x3900, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, // r +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E00, 0x3F80, 0x6180, 0x6000, 0x7F00, 0x3F80, 0x0180, 0x6180, 0x7F00, 0x1E00, 0x0000, 0x0000, 0x0000, // s +0x0000, 0x0000, 0x0800, 0x1800, 0x1800, 0x7F00, 0x7F00, 0x1800, 0x1800, 0x1800, 0x1800, 0x1800, 0x1800, 0x1F80, 0x0F80, 0x0000, 0x0000, 0x0000, // t +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6180, 0x6380, 0x7F80, 0x3D80, 0x0000, 0x0000, 0x0000, // u +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x60C0, 0x3180, 0x3180, 0x3180, 0x1B00, 0x1B00, 0x1B00, 0x0E00, 0x0E00, 0x0600, 0x0000, 0x0000, 0x0000, // v +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xDD80, 0xDD80, 0xDD80, 0x5500, 0x5500, 0x5500, 0x7700, 0x7700, 0x2200, 0x2200, 0x0000, 0x0000, 0x0000, // w +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6180, 0x3300, 0x3300, 0x1E00, 0x0C00, 0x0C00, 0x1E00, 0x3300, 0x3300, 0x6180, 0x0000, 0x0000, 0x0000, // x +0x0000, 0x0000, 0x0000, 0x0000, 0x6180, 0x6180, 0x3180, 0x3300, 0x3300, 0x1B00, 0x1B00, 0x1B00, 0x0E00, 0x0E00, 0x0E00, 0x1C00, 0x7C00, 0x7000, // y +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7FC0, 0x7FC0, 0x0180, 0x0300, 0x0600, 0x0C00, 0x1800, 0x3000, 0x7FC0, 0x7FC0, 0x0000, 0x0000, 0x0000, // z +0x0380, 0x0780, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0E00, 0x1C00, 0x1C00, 0x0E00, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0780, 0x0380, // { +0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, 0x0600, // | +0x3800, 0x3C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0E00, 0x0700, 0x0700, 0x0E00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x3C00, 0x3800, // } +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3880, 0x7F80, 0x4700, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ~ +}; + +static const uint16_t Font16x26 [] = { +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [ ] +0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03C0,0x03C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x0000,0x0000,0x0000,0x03E0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [!] +0x1E3C,0x1E3C,0x1E3C,0x1E3C,0x1E3C,0x1E3C,0x1E3C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = ["] +0x01CE,0x03CE,0x03DE,0x039E,0x039C,0x079C,0x3FFF,0x7FFF,0x0738,0x0F38,0x0F78,0x0F78,0x0E78,0xFFFF,0xFFFF,0x1EF0,0x1CF0,0x1CE0,0x3CE0,0x3DE0,0x39E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [#] +0x03FC,0x0FFE,0x1FEE,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1FE0,0x0FE0,0x07E0,0x03F0,0x01FC,0x01FE,0x01FE,0x01FE,0x01FE,0x01FE,0x01FE,0x3DFE,0x3FFC,0x0FF0,0x01E0,0x01E0,0x0000,0x0000,0x0000, // Ascii = [$] +0x3E03,0xF707,0xE78F,0xE78E,0xE39E,0xE3BC,0xE7B8,0xE7F8,0xF7F0,0x3FE0,0x01C0,0x03FF,0x07FF,0x07F3,0x0FF3,0x1EF3,0x3CF3,0x38F3,0x78F3,0xF07F,0xE03F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [%] +0x07E0,0x0FF8,0x0F78,0x1F78,0x1F78,0x1F78,0x0F78,0x0FF0,0x0FE0,0x1F80,0x7FC3,0xFBC3,0xF3E7,0xF1F7,0xF0F7,0xF0FF,0xF07F,0xF83E,0x7C7F,0x3FFF,0x1FEF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [&] +0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03C0,0x01C0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = ['] +0x003F,0x007C,0x01F0,0x01E0,0x03C0,0x07C0,0x0780,0x0780,0x0F80,0x0F00,0x0F00,0x0F00,0x0F00,0x0F00,0x0F00,0x0F80,0x0780,0x0780,0x07C0,0x03C0,0x01E0,0x01F0,0x007C,0x003F,0x000F,0x0000, // Ascii = [(] +0x7E00,0x1F00,0x07C0,0x03C0,0x01E0,0x01F0,0x00F0,0x00F0,0x00F8,0x0078,0x0078,0x0078,0x0078,0x0078,0x0078,0x00F8,0x00F0,0x00F0,0x01F0,0x01E0,0x03C0,0x07C0,0x1F00,0x7E00,0x7800,0x0000, // Ascii = [)] +0x03E0,0x03C0,0x01C0,0x39CE,0x3FFF,0x3F7F,0x0320,0x0370,0x07F8,0x0F78,0x1F3C,0x0638,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [*] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0xFFFF,0xFFFF,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [+] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03E0,0x03E0,0x03E0,0x03E0,0x01E0,0x01E0,0x01E0,0x01C0,0x0380, // Ascii = [,] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3FFE,0x3FFE,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [-] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03E0,0x03E0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [.] +0x000F,0x000F,0x001E,0x001E,0x003C,0x003C,0x0078,0x0078,0x00F0,0x00F0,0x01E0,0x01E0,0x03C0,0x03C0,0x0780,0x0780,0x0F00,0x0F00,0x1E00,0x1E00,0x3C00,0x3C00,0x7800,0x7800,0xF000,0x0000, // Ascii = [/] +0x07F0,0x0FF8,0x1F7C,0x3E3E,0x3C1E,0x7C1F,0x7C1F,0x780F,0x780F,0x780F,0x780F,0x780F,0x780F,0x780F,0x7C1F,0x7C1F,0x3C1E,0x3E3E,0x1F7C,0x0FF8,0x07F0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [0] +0x00F0,0x07F0,0x3FF0,0x3FF0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x3FFF,0x3FFF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [1] +0x0FE0,0x3FF8,0x3C7C,0x003C,0x003E,0x003E,0x003E,0x003C,0x003C,0x007C,0x00F8,0x01F0,0x03E0,0x07C0,0x0780,0x0F00,0x1E00,0x3E00,0x3C00,0x3FFE,0x3FFE,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [2] +0x0FF0,0x1FF8,0x1C7C,0x003E,0x003E,0x003E,0x003C,0x003C,0x00F8,0x0FF0,0x0FF8,0x007C,0x003E,0x001E,0x001E,0x001E,0x001E,0x003E,0x1C7C,0x1FF8,0x1FE0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [3] +0x0078,0x00F8,0x00F8,0x01F8,0x03F8,0x07F8,0x07F8,0x0F78,0x1E78,0x1E78,0x3C78,0x7878,0x7878,0xFFFF,0xFFFF,0x0078,0x0078,0x0078,0x0078,0x0078,0x0078,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [4] +0x1FFC,0x1FFC,0x1FFC,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1FE0,0x1FF8,0x00FC,0x007C,0x003E,0x003E,0x001E,0x003E,0x003E,0x003C,0x1C7C,0x1FF8,0x1FE0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [5] +0x01FC,0x07FE,0x0F8E,0x1F00,0x1E00,0x3E00,0x3C00,0x3C00,0x3DF8,0x3FFC,0x7F3E,0x7E1F,0x3C0F,0x3C0F,0x3C0F,0x3C0F,0x3E0F,0x1E1F,0x1F3E,0x0FFC,0x03F0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [6] +0x3FFF,0x3FFF,0x3FFF,0x000F,0x001E,0x001E,0x003C,0x0038,0x0078,0x00F0,0x00F0,0x01E0,0x01E0,0x03C0,0x03C0,0x0780,0x0F80,0x0F80,0x0F00,0x1F00,0x1F00,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [7] +0x07F8,0x0FFC,0x1F3E,0x1E1E,0x3E1E,0x3E1E,0x1E1E,0x1F3C,0x0FF8,0x07F0,0x0FF8,0x1EFC,0x3E3E,0x3C1F,0x7C1F,0x7C0F,0x7C0F,0x3C1F,0x3F3E,0x1FFC,0x07F0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [8] +0x07F0,0x0FF8,0x1E7C,0x3C3E,0x3C1E,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x3C1F,0x3E3F,0x1FFF,0x07EF,0x001F,0x001E,0x001E,0x003E,0x003C,0x38F8,0x3FF0,0x1FE0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [9] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03E0,0x03E0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03E0,0x03E0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [:] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03E0,0x03E0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03E0,0x03E0,0x03E0,0x03E0,0x01E0,0x01E0,0x01E0,0x03C0,0x0380, // Ascii = [;] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0003,0x000F,0x003F,0x00FC,0x03F0,0x0FC0,0x3F00,0xFE00,0x3F00,0x0FC0,0x03F0,0x00FC,0x003F,0x000F,0x0003,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [<] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [=] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xE000,0xF800,0x7E00,0x1F80,0x07E0,0x01F8,0x007E,0x001F,0x007E,0x01F8,0x07E0,0x1F80,0x7E00,0xF800,0xE000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [>] +0x1FF0,0x3FFC,0x383E,0x381F,0x381F,0x001E,0x001E,0x003C,0x0078,0x00F0,0x01E0,0x03C0,0x03C0,0x07C0,0x07C0,0x0000,0x0000,0x0000,0x07C0,0x07C0,0x07C0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [?] +0x03F8,0x0FFE,0x1F1E,0x3E0F,0x3C7F,0x78FF,0x79EF,0x73C7,0xF3C7,0xF38F,0xF38F,0xF38F,0xF39F,0xF39F,0x73FF,0x7BFF,0x79F7,0x3C00,0x1F1C,0x0FFC,0x03F8,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [@] +0x0000,0x0000,0x0000,0x03E0,0x03E0,0x07F0,0x07F0,0x07F0,0x0F78,0x0F78,0x0E7C,0x1E3C,0x1E3C,0x3C3E,0x3FFE,0x3FFF,0x781F,0x780F,0xF00F,0xF007,0xF007,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [A] +0x0000,0x0000,0x0000,0x3FF8,0x3FFC,0x3C3E,0x3C1E,0x3C1E,0x3C1E,0x3C3E,0x3C7C,0x3FF0,0x3FF8,0x3C7E,0x3C1F,0x3C1F,0x3C0F,0x3C0F,0x3C1F,0x3FFE,0x3FF8,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [B] +0x0000,0x0000,0x0000,0x01FF,0x07FF,0x1F87,0x3E00,0x3C00,0x7C00,0x7800,0x7800,0x7800,0x7800,0x7800,0x7C00,0x7C00,0x3E00,0x3F00,0x1F83,0x07FF,0x01FF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [C] +0x0000,0x0000,0x0000,0x7FF0,0x7FFC,0x787E,0x781F,0x781F,0x780F,0x780F,0x780F,0x780F,0x780F,0x780F,0x780F,0x780F,0x781F,0x781E,0x787E,0x7FF8,0x7FE0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [D] +0x0000,0x0000,0x0000,0x3FFF,0x3FFF,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3FFE,0x3FFE,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3FFF,0x3FFF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [E] +0x0000,0x0000,0x0000,0x1FFF,0x1FFF,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1FFF,0x1FFF,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [F] +0x0000,0x0000,0x0000,0x03FE,0x0FFF,0x1F87,0x3E00,0x7C00,0x7C00,0x7800,0xF800,0xF800,0xF87F,0xF87F,0x780F,0x7C0F,0x7C0F,0x3E0F,0x1F8F,0x0FFF,0x03FE,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [G] +0x0000,0x0000,0x0000,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7FFF,0x7FFF,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x7C1F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [H] +0x0000,0x0000,0x0000,0x3FFF,0x3FFF,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x3FFF,0x3FFF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [I] +0x0000,0x0000,0x0000,0x1FFC,0x1FFC,0x007C,0x007C,0x007C,0x007C,0x007C,0x007C,0x007C,0x007C,0x007C,0x007C,0x007C,0x0078,0x0078,0x38F8,0x3FF0,0x3FC0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [J] +0x0000,0x0000,0x0000,0x3C1F,0x3C1E,0x3C3C,0x3C78,0x3CF0,0x3DE0,0x3FE0,0x3FC0,0x3F80,0x3FC0,0x3FE0,0x3DF0,0x3CF0,0x3C78,0x3C7C,0x3C3E,0x3C1F,0x3C0F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [K] +0x0000,0x0000,0x0000,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3FFF,0x3FFF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [L] +0x0000,0x0000,0x0000,0xF81F,0xFC1F,0xFC1F,0xFE3F,0xFE3F,0xFE3F,0xFF7F,0xFF77,0xFF77,0xF7F7,0xF7E7,0xF3E7,0xF3E7,0xF3C7,0xF007,0xF007,0xF007,0xF007,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [M] +0x0000,0x0000,0x0000,0x7C0F,0x7C0F,0x7E0F,0x7F0F,0x7F0F,0x7F8F,0x7F8F,0x7FCF,0x7BEF,0x79EF,0x79FF,0x78FF,0x78FF,0x787F,0x783F,0x783F,0x781F,0x781F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [N] +0x0000,0x0000,0x0000,0x07F0,0x1FFC,0x3E3E,0x7C1F,0x780F,0x780F,0xF80F,0xF80F,0xF80F,0xF80F,0xF80F,0xF80F,0x780F,0x780F,0x7C1F,0x3E3E,0x1FFC,0x07F0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [O] +0x0000,0x0000,0x0000,0x3FFC,0x3FFF,0x3E1F,0x3E0F,0x3E0F,0x3E0F,0x3E0F,0x3E1F,0x3E3F,0x3FFC,0x3FF0,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x3E00,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [P] +0x0000,0x0000,0x0000,0x07F0,0x1FFC,0x3E3E,0x7C1F,0x780F,0x780F,0xF80F,0xF80F,0xF80F,0xF80F,0xF80F,0xF80F,0x780F,0x780F,0x7C1F,0x3E3E,0x1FFC,0x07F8,0x007C,0x003F,0x000F,0x0003,0x0000, // Ascii = [Q] +0x0000,0x0000,0x0000,0x3FF0,0x3FFC,0x3C7E,0x3C3E,0x3C1E,0x3C1E,0x3C3E,0x3C3C,0x3CFC,0x3FF0,0x3FE0,0x3DF0,0x3CF8,0x3C7C,0x3C3E,0x3C1E,0x3C1F,0x3C0F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [R] +0x0000,0x0000,0x0000,0x07FC,0x1FFE,0x3E0E,0x3C00,0x3C00,0x3C00,0x3E00,0x1FC0,0x0FF8,0x03FE,0x007F,0x001F,0x000F,0x000F,0x201F,0x3C3E,0x3FFC,0x1FF0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [S] +0x0000,0x0000,0x0000,0xFFFF,0xFFFF,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [T] +0x0000,0x0000,0x0000,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x7C0F,0x3C1E,0x3C1E,0x3E3E,0x1FFC,0x07F0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [U] +0x0000,0x0000,0x0000,0xF007,0xF007,0xF807,0x780F,0x7C0F,0x3C1E,0x3C1E,0x3E1E,0x1E3C,0x1F3C,0x1F78,0x0F78,0x0FF8,0x07F0,0x07F0,0x07F0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [V] +0x0000,0x0000,0x0000,0xE003,0xF003,0xF003,0xF007,0xF3E7,0xF3E7,0xF3E7,0x73E7,0x7BF7,0x7FF7,0x7FFF,0x7F7F,0x7F7F,0x7F7E,0x3F7E,0x3E3E,0x3E3E,0x3E3E,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [W] +0x0000,0x0000,0x0000,0xF807,0x7C0F,0x3E1E,0x3E3E,0x1F3C,0x0FF8,0x07F0,0x07E0,0x03E0,0x03E0,0x07F0,0x0FF8,0x0F7C,0x1E7C,0x3C3E,0x781F,0x780F,0xF00F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [X] +0x0000,0x0000,0x0000,0xF807,0x7807,0x7C0F,0x3C1E,0x3E1E,0x1F3C,0x0F78,0x0FF8,0x07F0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [Y] +0x0000,0x0000,0x0000,0x7FFF,0x7FFF,0x000F,0x001F,0x003E,0x007C,0x00F8,0x00F0,0x01E0,0x03E0,0x07C0,0x0F80,0x0F00,0x1E00,0x3E00,0x7C00,0x7FFF,0x7FFF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [Z] +0x07FF,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x07FF,0x07FF,0x0000, // Ascii = [[] +0x7800,0x7800,0x3C00,0x3C00,0x1E00,0x1E00,0x0F00,0x0F00,0x0780,0x0780,0x03C0,0x03C0,0x01E0,0x01E0,0x00F0,0x00F0,0x0078,0x0078,0x003C,0x003C,0x001E,0x001E,0x000F,0x000F,0x0007,0x0000, // Ascii = [\] +0x7FF0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x00F0,0x7FF0,0x7FF0,0x0000, // Ascii = []] +0x00C0,0x01C0,0x01C0,0x03E0,0x03E0,0x07F0,0x07F0,0x0778,0x0F78,0x0F38,0x1E3C,0x1E3C,0x3C1E,0x3C1E,0x380F,0x780F,0x7807,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [^] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0000, // Ascii = [_] +0x00F0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [`] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0FF8,0x3FFC,0x3C7C,0x003E,0x003E,0x003E,0x07FE,0x1FFE,0x3E3E,0x7C3E,0x783E,0x7C3E,0x7C7E,0x3FFF,0x1FCF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [a] +0x3C00,0x3C00,0x3C00,0x3C00,0x3C00,0x3C00,0x3DF8,0x3FFE,0x3F3E,0x3E1F,0x3C0F,0x3C0F,0x3C0F,0x3C0F,0x3C0F,0x3C0F,0x3C1F,0x3C1E,0x3F3E,0x3FFC,0x3BF0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [b] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03FE,0x0FFF,0x1F87,0x3E00,0x3E00,0x3C00,0x7C00,0x7C00,0x7C00,0x3C00,0x3E00,0x3E00,0x1F87,0x0FFF,0x03FE,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [c] +0x001F,0x001F,0x001F,0x001F,0x001F,0x001F,0x07FF,0x1FFF,0x3E3F,0x3C1F,0x7C1F,0x7C1F,0x7C1F,0x781F,0x781F,0x7C1F,0x7C1F,0x3C3F,0x3E7F,0x1FFF,0x0FDF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [d] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03F8,0x0FFC,0x1F3E,0x3E1E,0x3C1F,0x7C1F,0x7FFF,0x7FFF,0x7C00,0x7C00,0x3C00,0x3E00,0x1F07,0x0FFF,0x03FE,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [e] +0x01FF,0x03E1,0x03C0,0x07C0,0x07C0,0x07C0,0x7FFF,0x7FFF,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x07C0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [f] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x07EF,0x1FFF,0x3E7F,0x3C1F,0x7C1F,0x7C1F,0x781F,0x781F,0x781F,0x7C1F,0x7C1F,0x3C3F,0x3E7F,0x1FFF,0x0FDF,0x001E,0x001E,0x001E,0x387C,0x3FF8, // Ascii = [g] +0x3C00,0x3C00,0x3C00,0x3C00,0x3C00,0x3C00,0x3DFC,0x3FFE,0x3F9E,0x3F1F,0x3E1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [h] +0x01F0,0x01F0,0x0000,0x0000,0x0000,0x0000,0x7FE0,0x7FE0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [i] +0x00F8,0x00F8,0x0000,0x0000,0x0000,0x0000,0x3FF8,0x3FF8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F8,0x00F0,0x71F0,0x7FE0, // Ascii = [j] +0x3C00,0x3C00,0x3C00,0x3C00,0x3C00,0x3C00,0x3C1F,0x3C3E,0x3C7C,0x3CF8,0x3DF0,0x3DE0,0x3FC0,0x3FC0,0x3FE0,0x3DF0,0x3CF8,0x3C7C,0x3C3E,0x3C1F,0x3C1F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [k] +0x7FF0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x01F0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [l] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xF79E,0xFFFF,0xFFFF,0xFFFF,0xFBE7,0xF9E7,0xF1C7,0xF1C7,0xF1C7,0xF1C7,0xF1C7,0xF1C7,0xF1C7,0xF1C7,0xF1C7,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [m] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3DFC,0x3FFE,0x3F9E,0x3F1F,0x3E1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x3C1F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [n] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x07F0,0x1FFC,0x3E3E,0x3C1F,0x7C1F,0x780F,0x780F,0x780F,0x780F,0x780F,0x7C1F,0x3C1F,0x3E3E,0x1FFC,0x07F0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [o] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3DF8,0x3FFE,0x3F3E,0x3E1F,0x3C0F,0x3C0F,0x3C0F,0x3C0F,0x3C0F,0x3C0F,0x3C1F,0x3E1E,0x3F3E,0x3FFC,0x3FF8,0x3C00,0x3C00,0x3C00,0x3C00,0x3C00, // Ascii = [p] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x07EE,0x1FFE,0x3E7E,0x3C1E,0x7C1E,0x781E,0x781E,0x781E,0x781E,0x781E,0x7C1E,0x7C3E,0x3E7E,0x1FFE,0x0FDE,0x001E,0x001E,0x001E,0x001E,0x001E, // Ascii = [q] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1F7F,0x1FFF,0x1FE7,0x1FC7,0x1F87,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [r] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x07FC,0x1FFE,0x1E0E,0x3E00,0x3E00,0x3F00,0x1FE0,0x07FC,0x00FE,0x003E,0x001E,0x001E,0x3C3E,0x3FFC,0x1FF0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [s] +0x0000,0x0000,0x0000,0x0780,0x0780,0x0780,0x7FFF,0x7FFF,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x0780,0x07C0,0x03FF,0x01FF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [t] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3C1E,0x3C1E,0x3C1E,0x3C1E,0x3C1E,0x3C1E,0x3C1E,0x3C1E,0x3C1E,0x3C1E,0x3C3E,0x3C7E,0x3EFE,0x1FFE,0x0FDE,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [u] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xF007,0x780F,0x780F,0x3C1E,0x3C1E,0x3E1E,0x1E3C,0x1E3C,0x0F78,0x0F78,0x0FF0,0x07F0,0x07F0,0x03E0,0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [v] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xF003,0xF1E3,0xF3E3,0xF3E7,0xF3F7,0xF3F7,0x7FF7,0x7F77,0x7F7F,0x7F7F,0x7F7F,0x3E3E,0x3E3E,0x3E3E,0x3E3E,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [w] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7C0F,0x3E1E,0x3E3C,0x1F3C,0x0FF8,0x07F0,0x07F0,0x03E0,0x07F0,0x07F8,0x0FF8,0x1E7C,0x3E3E,0x3C1F,0x781F,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [x] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xF807,0x780F,0x7C0F,0x3C1E,0x3C1E,0x1E3C,0x1E3C,0x1F3C,0x0F78,0x0FF8,0x07F0,0x07F0,0x03E0,0x03E0,0x03C0,0x03C0,0x03C0,0x0780,0x0F80,0x7F00, // Ascii = [y] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3FFF,0x3FFF,0x001F,0x003E,0x007C,0x00F8,0x01F0,0x03E0,0x07C0,0x0F80,0x1F00,0x1E00,0x3C00,0x7FFF,0x7FFF,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [z] +0x01FE,0x03E0,0x03C0,0x03C0,0x03C0,0x03C0,0x01E0,0x01E0,0x01E0,0x01C0,0x03C0,0x3F80,0x3F80,0x03C0,0x01C0,0x01E0,0x01E0,0x01E0,0x03C0,0x03C0,0x03C0,0x03C0,0x03E0,0x01FE,0x007E,0x0000, // Ascii = [{] +0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x01C0,0x0000, // Ascii = [|] +0x3FC0,0x03E0,0x01E0,0x01E0,0x01E0,0x01E0,0x01C0,0x03C0,0x03C0,0x01C0,0x01E0,0x00FE,0x00FE,0x01E0,0x01C0,0x03C0,0x03C0,0x01C0,0x01E0,0x01E0,0x01E0,0x01E0,0x03E0,0x3FC0,0x3F00,0x0000, // Ascii = [}] +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3F07,0x7FC7,0x73E7,0xF1FF,0xF07E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Ascii = [~] +}; + + +FontDef Font_7x10 = {7,10,Font7x10}; +FontDef Font_11x18 = {11,18,Font11x18}; +FontDef Font_16x26 = {16,26,Font16x26}; diff --git a/lib/board/ssd1351/ssd1351.c b/lib/board/ssd1351/ssd1351.c new file mode 100644 index 000000000..177f8d302 --- /dev/null +++ b/lib/board/ssd1351/ssd1351.c @@ -0,0 +1,329 @@ +#include + +#ifndef EMULATOR +#include +#include +#include +#endif + +#include "keepkey/board/ssd1351/ssd1351.h" +#include "keepkey/board/pin.h" +#include "keepkey/board/timer.h" + + +static void SSD1351_Select(void) { + CLEAR_PIN(SSD1351_CS_Pin); +} + +void SSD1351_Unselect(void) { + SET_PIN(SSD1351_CS_Pin); +} + + +// All displays are reset with the same pin at the same time in display_reset(void) +// static void SSD1351_Reset(void) { +// SET_PIN(SSD1351_RES_Pin); +// delay_ms(1); +// CLEAR_PIN(SSD1351_RES_Pin); +// delay_ms(1); +// SET_PIN(SSD1351_RES_Pin); +// delay_ms(1); +// } + +static void SSD1351_WriteCommand(uint8_t cmd) { +#ifndef EMULATOR + + /* Set nDC low */ + CLEAR_PIN(SSD1351_DC_Pin); // low for cmd + + spi_send(SSD1351_SPI_PORT, (uint16_t)cmd); + delay_us(10); // apparently spi_send doesn't wait for buffer to finish as it should +#endif +} + +static void SSD1351_WriteData(uint8_t* buff, size_t buff_size) { + size_t bctr = 0; + SET_PIN(SSD1351_DC_Pin); // hi for data + + while(bctr < buff_size) { + spi_send(SSD1351_SPI_PORT, (uint16_t)buff[bctr]); + delay_us(10); + bctr++; + } + +} + +static void SSD1351_SetAddressWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + // column address set + SSD1351_WriteCommand(0x15); // SETCOLUMN + { + uint8_t data[] = { x0 & 0xFF, x1 & 0xFF }; + SSD1351_WriteData(data, sizeof(data)); + } + + // row address set + SSD1351_WriteCommand(0x75); // SETROW + { + uint8_t data[] = { y0 & 0xFF, y1 & 0xFF }; + SSD1351_WriteData(data, sizeof(data)); + } + + // write to RAM + SSD1351_WriteCommand(0x5C); // WRITERAM +} + +void SSD1351_CSInit(void) { + // set up chip select pin + gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + GPIO4); + + gpio_set_output_options(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, + GPIO4); + return; +} + +void SSD1351_Init() { + + SET_PIN(SSD1351_DC_Pin); + SSD1351_Select(); + + // command list is based on https://github.com/adafruit/Adafruit-SSD1351-library + + SSD1351_WriteCommand(0xFD); // COMMANDLOCK + { + uint8_t data[] = { 0x12 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xFD); // COMMANDLOCK + { + uint8_t data[] = { 0xB1 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xAE); // DISPLAYOFF + + // SSD1351_WriteCommand(0xB2); // DISPLAY ENHANCEMENT + // { + // uint8_t data[] = { 0XA4, 0X00, 0X00 }; // 127 + // SSD1351_WriteData(data, sizeof(data)); + // } + + SSD1351_WriteCommand(0xB3); // CLOCKDIV + { + uint8_t data[] = { 0xF1 };// 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio (A[3:0]+1 = 1..16) + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xCA); // MUXRATIO + { + uint8_t data[] = { 0x7F }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xA0); // SETREMAP + { + uint8_t data[] = { 0x26 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0x15); // column + { + uint8_t data[] = { 0x00, 0x7F }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0x75); // row + { + uint8_t data[] = { 0x00, 0x7F }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xA1); // startline + { + uint8_t data[] = { 0x00 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xA2); // DISPLAYOFFSET + { + uint8_t data[] = { 0x00 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xB5); // SETGPIO + { + uint8_t data[] = { 0x00 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xAB); // FUNCTIONSELECT + { + uint8_t data[] = { 0x01 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xB1); // PRECHARGE + { + uint8_t data[] = { 0x32 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xBE); // VCOMH + { + uint8_t data[] = { 0x05 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xA6); // NORMALDISPLAY (don't invert) + SSD1351_WriteCommand(0xC1); // CONTRASTABC + { + uint8_t data[] = { 0xC8, 0x80, 0xC8 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xC7); // CONTRASTMASTER + { + uint8_t data[] = { 0x0F }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xB4); // SETVSL + { + uint8_t data[] = { 0xA0, 0xB5, 0x55 }; + SSD1351_WriteData(data, sizeof(data)); + } + SSD1351_WriteCommand(0xB6); // PRECHARGE2 + { + uint8_t data[] = { 0x01 }; + SSD1351_WriteData(data, sizeof(data)); + } + + SSD1351_WriteCommand(0xAF); // DISPLAYON + SSD1351_Unselect(); +} + +void SSD1351_DrawPixel(uint16_t x, uint16_t y, uint16_t color) { + if((x >= SSD1351_WIDTH) || (y >= SSD1351_HEIGHT)) + return; + SSD1351_Select(); + SSD1351_SetAddressWindow(x, y, x+1, y+1); + uint8_t data[] = { color >> 8, color & 0xFF }; + SSD1351_WriteData(data, sizeof(data)); + SSD1351_Unselect(); +} + +static void SSD1351_WriteChar(uint16_t x, uint16_t y, char ch, FontDef font, uint16_t color, uint16_t bgcolor) { + uint32_t i, b, j; + + SSD1351_SetAddressWindow(x, y, x+font.width-1, y+font.height-1); + + for(i = 0; i < font.height; i++) { + b = font.data[(ch - 32) * font.height + i]; + for(j = 0; j < font.width; j++) { + if((b << j) & 0x8000) { + uint8_t data[] = { color >> 8, color & 0xFF }; + SSD1351_WriteData(data, sizeof(data)); + } else { + uint8_t data[] = { bgcolor >> 8, bgcolor & 0xFF }; + SSD1351_WriteData(data, sizeof(data)); + } + } + } +} + +void SSD1351_WriteString(uint16_t x, uint16_t y, const char* str, FontDef font, uint16_t color, uint16_t bgcolor) { + SSD1351_Select(); + + while(*str) { + if(x + font.width >= SSD1351_WIDTH) { + x = 0; + y += font.height; + if(y + font.height >= SSD1351_HEIGHT) { + break; + } + + if(*str == ' ') { + // skip spaces in the beginning of the new line + str++; + continue; + } + } + + SSD1351_WriteChar(x, y, *str, font, color, bgcolor); + x += font.width; + str++; + } + SSD1351_Unselect(); +} + +void SSD1351_FillRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { + // clipping + if((x >= SSD1351_WIDTH) || (y >= SSD1351_HEIGHT)) return; + if((x + w - 1) >= SSD1351_WIDTH) w = SSD1351_WIDTH - x; + if((y + h - 1) >= SSD1351_HEIGHT) h = SSD1351_HEIGHT - y; + + SSD1351_Select(); + SSD1351_SetAddressWindow(x, y, x+w-1, y+h-1); + + uint8_t data[] = { color >> 8, color & 0xFF }; + SET_PIN(SSD1351_DC_Pin); + for(y = h; y > 0; y--) { + for(x = w; x > 0; x--) { + SSD1351_WriteData(data, sizeof(data)); + } + } + SSD1351_Unselect(); +} + +void SSD1351_FillScreen(uint16_t color) { + SSD1351_FillRectangle(0, 0, SSD1351_WIDTH, SSD1351_HEIGHT, color); +} + +void SSD1351_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data) { + if((x >= SSD1351_WIDTH) || (y >= SSD1351_HEIGHT)) return; + if((x + w - 1) >= SSD1351_WIDTH) return; + if((y + h - 1) >= SSD1351_HEIGHT) return; + + + SSD1351_Select(); + SSD1351_SetAddressWindow(x, y, x+w-1, y+h-1); + SSD1351_WriteData((uint8_t*)data, sizeof(uint16_t)*w*h); + SSD1351_Unselect(); + +} + +void SSD1351_InvertColors(bool invert) { + SSD1351_Select(); + SSD1351_WriteCommand(invert ? 0xA7 /* INVERTDISPLAY */ : 0xA6 /* NORMALDISPLAY */); + SSD1351_Unselect(); + +} + +void SSD1351_SetRotation(uint8_t r) { + // madctl bits: + // 6,7 Color depth (01 = 64K) + // 5 Odd/even split COM (0: disable, 1: enable) + // 4 Scan direction (0: top-down, 1: bottom-up) + // 3 Reserved + // 2 Color remap (0: A->B->C, 1: C->B->A) + // 1 Column remap (0: 0-127, 1: 127-0) + // 0 Address increment (0: horizontal, 1: vertical) + uint8_t madctl = 0b01100100; // 64K, enable split, CBA + + uint8_t rotation = r & 3; // Clip input to valid range + + switch (rotation) { + case 0: + madctl |= 0b00010000; // Scan bottom-up + break; + case 1: + madctl |= 0b00010011; // Scan bottom-up, column remap 127-0, vertical + break; + case 2: + madctl |= 0b00000010; // Column remap 127-0 + break; + case 3: + madctl |= 0b00000001; // Vertical + + break; + } + + SSD1351_WriteCommand(0xA0); // SETREMAP + { + uint8_t data[] = { madctl }; + SSD1351_WriteData(data, sizeof(data)); + } + + uint8_t startline = (rotation < 2) ? SSD1351_HEIGHT : 0; + SSD1351_WriteCommand(0xA1); // STARTLINE + { + uint8_t data[] = { startline }; + SSD1351_WriteData(data, sizeof(data)); + } +} diff --git a/lib/board/timer.c b/lib/board/timer.c index 301d4f467..73e8ad572 100644 --- a/lib/board/timer.c +++ b/lib/board/timer.c @@ -19,7 +19,11 @@ #ifndef EMULATOR #include +#ifdef DEV_DEBUG +#include +#else #include +#endif #include #include #else @@ -31,7 +35,11 @@ #include "keepkey/board/keepkey_leds.h" #include "keepkey/board/timer.h" #include "keepkey/board/supervise.h" -#include "trezor/crypto/rand.h" +#include "hwcrypto/crypto/rand.h" + +#ifdef DEV_DEBUG +#include "keepkey/board/pin.h" +#endif #include @@ -217,23 +225,37 @@ void timer_init(void) { #ifndef EMULATOR // Set up the timer. rcc_periph_reset_pulse(RST_TIM4); - timer_enable_irq(TIM4, TIM_DIER_UIE); timer_set_mode(TIM4, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); +#ifdef DEV_DEBUG + /* set prescaler + 2*rcc_apb1_frequency is the internal clock freq for this timer, set for 10khz + */ + timer_set_prescaler(TIM4, 2*rcc_apb1_frequency/10000); + timer_set_period(TIM4, 10); // set for a timer period of 5 khz (empirically determined) + + // initialize scope trig here + pin_init_output(&SCOPE_PIN, PUSH_PULL_MODE, NO_PULL_MODE); + +#else /* 1000 * ( 120 / 12000000 ) = 1 ms intervals, where 1000 is the counter, 120 is the prescalar, - and 12000000 is the clks/second */ + and 12000000 is the clks/second + THIS IS INCORRECT, PRESCALER CAN ONLY BE A 16 BIT NUMBER*/ timer_set_prescaler(TIM4, 120000); timer_set_period(TIM4, 1); +#endif // DEV_DEBUG nvic_set_priority(NVIC_TIM4_IRQ, 16 * 2); timer_enable_counter(TIM4); -#else + timer_enable_irq(TIM4, TIM_DIER_UIE); + +#else //EMULATOR void tim4_sighandler(int sig); signal(SIGALRM, tim4_sighandler); ualarm(1000, 1000); -#endif +#endif // EMULATOR } uint32_t fi_defense_delay(volatile uint32_t value) { @@ -266,7 +288,11 @@ uint32_t fi_defense_delay(volatile uint32_t value) { */ void delay_us(uint32_t us) { #ifndef EMULATOR - uint32_t cnt = us * 20; +#ifdef DEV_DEBUG +uint32_t cnt = us * 28; // 168Mhz clock +#else +uint32_t cnt = us * 20; +#endif while (cnt--) { __asm__("nop"); @@ -285,7 +311,11 @@ void delay_us(uint32_t us) { * none */ void delay_ms(uint32_t ms) { - remaining_delay = ms; +#ifdef DEV_DEBUG +remaining_delay = 2*ms; // this is mult by 2 for 5Mhz clock +#else +remaining_delay = ms; // this is mult by 2 for 5Mhz clock +#endif while (remaining_delay > 0) { } @@ -345,9 +375,10 @@ void timerisr_usr(void) { run_runnables(); #ifndef EMULATOR + svc_tusr_return(); // this MUST be called last to properly clean up and // return -#endif +#endif // EMULATOR } #ifdef EMULATOR diff --git a/lib/board/usb.c b/lib/board/usb.c index 3b37dc96c..ea669805b 100644 --- a/lib/board/usb.c +++ b/lib/board/usb.c @@ -21,8 +21,6 @@ #include #include #include -#include -#include #include #include #include "keepkey/board/keepkey_board.h" @@ -431,7 +429,7 @@ char usbTiny(char set) { #endif // EMULATOR bool msg_write(MessageType msg_id, const void *msg) { - const pb_field_t *fields = message_fields(NORMAL_MSG, msg_id, OUT_MSG); + const pb_msgdesc_t *fields = message_fields(NORMAL_MSG, msg_id, OUT_MSG); if (!fields) return false; @@ -472,7 +470,7 @@ bool msg_write(MessageType msg_id, const void *msg) { #if DEBUG_LINK bool msg_debug_write(MessageType msg_id, const void *msg) { - const pb_field_t *fields = message_fields(DEBUG_MSG, msg_id, OUT_MSG); + const pb_msgdesc_t *fields = message_fields(DEBUG_MSG, msg_id, OUT_MSG); if (!fields) return false; diff --git a/lib/board/variant.c b/lib/board/variant.c index d996858ab..4c384233a 100644 --- a/lib/board/variant.c +++ b/lib/board/variant.c @@ -2,9 +2,9 @@ #include "keepkey/board/keepkey_flash.h" #include "keepkey/board/pubkeys.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/sha2.h" #include "keepkey/variant/keepkey.h" #include "keepkey/variant/salt.h" @@ -118,7 +118,11 @@ const VariantInfo *__attribute__((weak)) variant_getInfo(void) { case MODEL_KEEPKEY: return &variant_keepkey; case MODEL_SALT: +#ifndef BITCOIN_ONLY return &variant_salt; +#else + return &variant_keepkey; +#endif case MODEL_FOX: return &variant_keepkey; case MODEL_KASPERSKY: @@ -152,7 +156,11 @@ const VariantAnimation *variant_getLogo(bool reversed) { const char *variant_getName(void) { #ifdef EMULATOR - return "Emulator"; + #ifdef BITCOIN_ONLY + return "EmulatorBTC"; + #else + return "Emulator"; + #endif #else if (name) { return name; diff --git a/lib/firmware/CMakeLists.txt b/lib/firmware/CMakeLists.txt index e08ccfc69..3875a8822 100644 --- a/lib/firmware/CMakeLists.txt +++ b/lib/firmware/CMakeLists.txt @@ -2,45 +2,50 @@ set(sources app_confirm.c app_layout.c authenticator.c - binance.c coins.c crypto.c - eip712.c - eos.c - eos-contracts/eosio.system.c - eos-contracts/eosio.token.c - ethereum.c - ethereum_contracts.c - ethereum_contracts/makerdao.c - ethereum_contracts/saproxy.c - ethereum_contracts/zxappliquid.c - ethereum_contracts/thortx.c - ethereum_contracts/zxliquidtx.c - ethereum_contracts/zxtransERC20.c - ethereum_contracts/zxswap.c - ethereum_tokens.c fsm.c home_sm.c - mayachain.c - nano.c - osmosis.c passphrase_sm.c pin_sm.c policy.c recovery_cipher.c reset.c - ripple.c - ripple_base58.c signing.c - signtx_tendermint.c storage.c - tendermint.c - thorchain.c tiny-json.c transaction.c txin_check.c u2f.c) +if("${COIN_SUPPORT}" STREQUAL "BTC") +else() + list(APPEND sources + binance.c + eip712.c + eos.c + eos-contracts/eosio.system.c + eos-contracts/eosio.token.c + ethereum.c + ethereum_contracts.c + ethereum_contracts/makerdao.c + ethereum_contracts/saproxy.c + ethereum_contracts/zxappliquid.c + ethereum_contracts/thortx.c + ethereum_contracts/zxliquidtx.c + ethereum_contracts/zxtransERC20.c + ethereum_contracts/zxswap.c + ethereum_tokens.c + mayachain.c + nano.c + osmosis.c + ripple.c + ripple_base58.c + signtx_tendermint.c + tendermint.c + thorchain.c) +endif() + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_revision.h.in" "${CMAKE_CURRENT_BINARY_DIR}/scm_revision.h" @ONLY) @@ -51,14 +56,20 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR}) add_library(kkfirmware ${sources}) -add_dependencies(kkfirmware kktransport.pb trezorcrypto qrcodegenerator ethereum_tokens.def) -set(ETHEREUM_TOKENS ${CMAKE_BINARY_DIR}/include/keepkey/firmware/ethereum_tokens) -set(UNISWAP_TOKENS ${CMAKE_BINARY_DIR}/include/keepkey/firmware/uniswap_tokens) +if("${COIN_SUPPORT}" STREQUAL "BTC") + # for bitcoin-only, do not build eth token table or uniswap tokens + add_dependencies(kkfirmware kktransport.pb hwcrypto qrcodegenerator) + +else() + add_dependencies(kkfirmware kktransport.pb hwcrypto qrcodegenerator ethereum_tokens.def) + set(ETHEREUM_TOKENS ${CMAKE_BINARY_DIR}/include/keepkey/firmware/ethereum_tokens) + set(UNISWAP_TOKENS ${CMAKE_BINARY_DIR}/include/keepkey/firmware/uniswap_tokens) -add_custom_target(ethereum_tokens.def - COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/keepkey/firmware - COMMAND python3 ${CMAKE_SOURCE_DIR}/deps/python-keepkey/keepkeylib/eth/ethereum_tokens.py ${ETHEREUM_TOKENS}.def - COMMAND python3 ${CMAKE_SOURCE_DIR}/deps/python-keepkey/keepkeylib/eth/uniswap_tokens.py ${UNISWAP_TOKENS}.def) + add_custom_target(ethereum_tokens.def + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/keepkey/firmware + COMMAND python3 ${CMAKE_SOURCE_DIR}/deps/python-keepkey/keepkeylib/eth/ethereum_tokens.py ${ETHEREUM_TOKENS}.def + COMMAND python3 ${CMAKE_SOURCE_DIR}/deps/python-keepkey/keepkeylib/eth/uniswap_tokens.py ${UNISWAP_TOKENS}.def) +endif() add_library(kkfirmware.keepkey variant/keepkey/resources.c) diff --git a/lib/firmware/app_confirm.c b/lib/firmware/app_confirm.c index 27f059a4f..04587c73e 100644 --- a/lib/firmware/app_confirm.c +++ b/lib/firmware/app_confirm.c @@ -37,7 +37,7 @@ #include "keepkey/firmware/app_layout.h" #include "keepkey/firmware/coins.h" -#include "trezor/crypto/bignum.h" +#include "hwcrypto/crypto/bignum.h" #include #include diff --git a/lib/firmware/authenticator.c b/lib/firmware/authenticator.c index 922eae01c..631732442 100644 --- a/lib/firmware/authenticator.c +++ b/lib/firmware/authenticator.c @@ -23,11 +23,11 @@ #include "keepkey/firmware/authenticator.h" #include "keepkey/firmware/crypto.h" #include "keepkey/firmware/storage.h" -#include "trezor/crypto/aes/aes.h" -#include "trezor/crypto/base32.h" -#include "trezor/crypto/hmac.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/aes/aes.h" +#include "hwcrypto/crypto/base32.h" +#include "hwcrypto/crypto/hmac.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/sha2.h" #include "keepkey/firmware/app_confirm.h" #include "keepkey/firmware/app_layout.h" diff --git a/lib/firmware/binance.c b/lib/firmware/binance.c index 410de1a69..c27dece3b 100644 --- a/lib/firmware/binance.c +++ b/lib/firmware/binance.c @@ -1,9 +1,9 @@ #include "keepkey/firmware/binance.h" #include "keepkey/firmware/tendermint.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/segwit_addr.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/segwit_addr.h" #include "messages-binance.pb.h" diff --git a/lib/firmware/coins.c b/lib/firmware/coins.c index a2c859fff..257439e9c 100644 --- a/lib/firmware/coins.c +++ b/lib/firmware/coins.c @@ -85,6 +85,7 @@ const CoinType coins[COINS_COUNT] = { TAPROOT}, #include "keepkey/firmware/coins.def" +#ifndef BITCOIN_ONLY #define X(INDEX, NAME, SYMBOL, DECIMALS, CONTRACT_ADDRESS) \ { \ true, \ @@ -130,6 +131,7 @@ const CoinType coins[COINS_COUNT] = { false, false, /* has_taproot, taproot*/ \ }, #include "keepkey/firmware/tokens.def" +#endif // BITCOIN_ONLY }; _Static_assert(sizeof(coins) / sizeof(coins[0]) == COINS_COUNT, diff --git a/lib/firmware/crypto.c b/lib/firmware/crypto.c index 4d886a92e..edccf83f6 100644 --- a/lib/firmware/crypto.c +++ b/lib/firmware/crypto.c @@ -21,18 +21,18 @@ #include "keepkey/board/layout.h" #include "keepkey/firmware/coins.h" #include "keepkey/firmware/crypto.h" -#include "trezor/crypto/address.h" -#include "trezor/crypto/aes/aes.h" -#include "trezor/crypto/base58.h" -#include "trezor/crypto/bip32.h" -#include "trezor/crypto/cash_addr.h" -#include "trezor/crypto/curves.h" -#include "trezor/crypto/hmac.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/pbkdf2.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/segwit_addr.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/address.h" +#include "hwcrypto/crypto/aes/aes.h" +#include "hwcrypto/crypto/base58.h" +#include "hwcrypto/crypto/bip32.h" +#include "hwcrypto/crypto/cash_addr.h" +#include "hwcrypto/crypto/curves.h" +#include "hwcrypto/crypto/hmac.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/pbkdf2.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/segwit_addr.h" +#include "hwcrypto/crypto/sha2.h" #include diff --git a/lib/firmware/eip712.c b/lib/firmware/eip712.c index 25e14d3b1..16dd75960 100644 --- a/lib/firmware/eip712.c +++ b/lib/firmware/eip712.c @@ -41,8 +41,8 @@ #include "keepkey/firmware/eip712.h" #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/tiny-json.h" -#include "trezor/crypto/sha3.h" -#include "trezor/crypto/memzero.h" +#include "hwcrypto/crypto/sha3.h" +#include "hwcrypto/crypto/memzero.h" static const char *udefList[MAX_USERDEF_TYPES] = {0}; static dm confirmProp; diff --git a/lib/firmware/eos.c b/lib/firmware/eos.c index f3c0cce80..b46f8cc61 100644 --- a/lib/firmware/eos.c +++ b/lib/firmware/eos.c @@ -27,11 +27,11 @@ #include "keepkey/firmware/fsm.h" #include "keepkey/firmware/home_sm.h" #include "keepkey/firmware/storage.h" -#include "trezor/crypto/base58.h" -#include "trezor/crypto/bip32.h" -#include "trezor/crypto/hasher.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/secp256k1.h" +#include "hwcrypto/crypto/base58.h" +#include "hwcrypto/crypto/bip32.h" +#include "hwcrypto/crypto/hasher.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/secp256k1.h" #include "messages-eos.pb.h" diff --git a/lib/firmware/eos.h b/lib/firmware/eos.h index 811ed242a..638a64530 100644 --- a/lib/firmware/eos.h +++ b/lib/firmware/eos.h @@ -20,7 +20,7 @@ #ifndef LIB_FIRMWARE_EOS_H #define LIB_FIRMWARE_EOS_H -#include "trezor/crypto/hasher.h" +#include "hwcrypto/crypto/hasher.h" #define CHECK_PARAM_RET(cond, errormsg, retval) \ if (!(cond)) { \ diff --git a/lib/firmware/ethereum.c b/lib/firmware/ethereum.c index 2bdfc2426..84edf0381 100644 --- a/lib/firmware/ethereum.c +++ b/lib/firmware/ethereum.c @@ -38,11 +38,11 @@ #include "keepkey/firmware/thorchain.h" #include "keepkey/firmware/tiny-json.h" #include "keepkey/firmware/transaction.h" -#include "trezor/crypto/address.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/sha3.h" +#include "hwcrypto/crypto/address.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/sha3.h" #include diff --git a/lib/firmware/ethereum_contracts/makerdao.c b/lib/firmware/ethereum_contracts/makerdao.c index fa9890be6..b708644a0 100644 --- a/lib/firmware/ethereum_contracts/makerdao.c +++ b/lib/firmware/ethereum_contracts/makerdao.c @@ -24,7 +24,7 @@ #include "keepkey/firmware/ethereum.h" #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" -#include "trezor/crypto/address.h" +#include "hwcrypto/crypto/address.h" static bool getCupId(const uint8_t *param, uint32_t *val) { bignum256 value; diff --git a/lib/firmware/ethereum_contracts/saproxy.c b/lib/firmware/ethereum_contracts/saproxy.c index 43f395b84..8e6c38a63 100644 --- a/lib/firmware/ethereum_contracts/saproxy.c +++ b/lib/firmware/ethereum_contracts/saproxy.c @@ -24,7 +24,7 @@ #include "keepkey/firmware/ethereum.h" #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" -#include "trezor/crypto/address.h" +#include "hwcrypto/crypto/address.h" static bool isWithFromSalary(const EthereumSignTx *msg) { if (memcmp(msg->data_initial_chunk.bytes, "\xfe\xa7\xc5\x3f", 4) == 0) diff --git a/lib/firmware/ethereum_contracts/thortx.c b/lib/firmware/ethereum_contracts/thortx.c index 07f3b41f4..eba476100 100644 --- a/lib/firmware/ethereum_contracts/thortx.c +++ b/lib/firmware/ethereum_contracts/thortx.c @@ -25,7 +25,7 @@ #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" #include "keepkey/firmware/thorchain.h" -#include "trezor/crypto/address.h" +#include "hwcrypto/crypto/address.h" bool thor_isThorchainTx(const EthereumSignTx *msg) { if (msg->has_to && msg->to.size == 20 && diff --git a/lib/firmware/ethereum_contracts/zxappliquid.c b/lib/firmware/ethereum_contracts/zxappliquid.c index 7fa3db963..894bb7e26 100644 --- a/lib/firmware/ethereum_contracts/zxappliquid.c +++ b/lib/firmware/ethereum_contracts/zxappliquid.c @@ -28,11 +28,11 @@ #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" #include "keepkey/firmware/storage.h" -#include "trezor/crypto/address.h" -#include "trezor/crypto/bip32.h" -#include "trezor/crypto/curves.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/sha3.h" +#include "hwcrypto/crypto/address.h" +#include "hwcrypto/crypto/bip32.h" +#include "hwcrypto/crypto/curves.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/sha3.h" bool zx_confirmApproveLiquidity(uint32_t data_total, const EthereumSignTx *msg) { (void)data_total; diff --git a/lib/firmware/ethereum_contracts/zxliquidtx.c b/lib/firmware/ethereum_contracts/zxliquidtx.c index 1489913a4..a3925bbf0 100644 --- a/lib/firmware/ethereum_contracts/zxliquidtx.c +++ b/lib/firmware/ethereum_contracts/zxliquidtx.c @@ -27,11 +27,11 @@ #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" #include "keepkey/firmware/storage.h" -#include "trezor/crypto/address.h" -#include "trezor/crypto/bip32.h" -#include "trezor/crypto/curves.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/sha3.h" +#include "hwcrypto/crypto/address.h" +#include "hwcrypto/crypto/bip32.h" +#include "hwcrypto/crypto/curves.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/sha3.h" #include diff --git a/lib/firmware/ethereum_contracts/zxswap.c b/lib/firmware/ethereum_contracts/zxswap.c index fda00b8cd..012cd44c7 100644 --- a/lib/firmware/ethereum_contracts/zxswap.c +++ b/lib/firmware/ethereum_contracts/zxswap.c @@ -24,7 +24,7 @@ #include "keepkey/firmware/ethereum.h" #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" -#include "trezor/crypto/address.h" +#include "hwcrypto/crypto/address.h" static bool isSellToUniswapCall(const EthereumSignTx *msg) { if (memcmp(msg->data_initial_chunk.bytes, "\xd9\x62\x7a\xa4", 4) == 0) diff --git a/lib/firmware/ethereum_contracts/zxtransERC20.c b/lib/firmware/ethereum_contracts/zxtransERC20.c index 942dfef8b..149ac618d 100644 --- a/lib/firmware/ethereum_contracts/zxtransERC20.c +++ b/lib/firmware/ethereum_contracts/zxtransERC20.c @@ -24,7 +24,7 @@ #include "keepkey/firmware/ethereum.h" #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" -#include "trezor/crypto/address.h" +#include "hwcrypto/crypto/address.h" static bool isTransERC20Call(const EthereumSignTx *msg) { if (memcmp(msg->data_initial_chunk.bytes, "\x41\x55\x65\xb0", 4) == 0) diff --git a/lib/firmware/fsm.c b/lib/firmware/fsm.c index 0d779f448..4ac9dc6b7 100644 --- a/lib/firmware/fsm.c +++ b/lib/firmware/fsm.c @@ -17,6 +17,9 @@ * along with this library. If not, see . */ + +#include + #include "scm_revision.h" #include "variant.h" #include "u2f_knownapps.h" @@ -36,45 +39,48 @@ #include "keepkey/firmware/app_layout.h" #include "keepkey/firmware/authenticator.h" #include "keepkey/firmware/coins.h" -#include "keepkey/firmware/cosmos.h" -#include "keepkey/firmware/binance.h" #include "keepkey/firmware/crypto.h" -#include "keepkey/firmware/eos.h" -#include "keepkey/firmware/eos-contracts.h" -#include "keepkey/firmware/ethereum.h" -#include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" #include "keepkey/firmware/home_sm.h" -#include "keepkey/firmware/mayachain.h" -#include "keepkey/firmware/osmosis.h" #include "keepkey/firmware/passphrase_sm.h" #include "keepkey/firmware/pin_sm.h" #include "keepkey/firmware/policy.h" #include "keepkey/firmware/recovery_cipher.h" #include "keepkey/firmware/reset.h" -#include "keepkey/firmware/ripple.h" #include "keepkey/firmware/signing.h" -#include "keepkey/firmware/signtx_tendermint.h" #include "keepkey/firmware/storage.h" -#include "keepkey/firmware/tendermint.h" -#include "keepkey/firmware/thorchain.h" #include "keepkey/firmware/transaction.h" #include "keepkey/firmware/txin_check.h" #include "keepkey/firmware/u2f.h" #include "keepkey/rand/rng.h" -#include "trezor/crypto/address.h" -#include "trezor/crypto/aes/aes.h" -#include "trezor/crypto/base58.h" -#include "trezor/crypto/bip39.h" -#include "trezor/crypto/curves.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/hmac.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/rand.h" -#include "trezor/crypto/ripemd160.h" -#include "trezor/crypto/secp256k1.h" +#include "hwcrypto/crypto/address.h" +#include "hwcrypto/crypto/aes/aes.h" +#include "hwcrypto/crypto/base58.h" +#include "hwcrypto/crypto/bip39.h" +#include "hwcrypto/crypto/curves.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/hmac.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/rand.h" +#include "hwcrypto/crypto/ripemd160.h" +#include "hwcrypto/crypto/secp256k1.h" #include "messages.pb.h" + +#ifndef BITCOIN_ONLY +#include "keepkey/firmware/cosmos.h" +#include "keepkey/firmware/binance.h" +#include "keepkey/firmware/eos.h" +#include "keepkey/firmware/eos-contracts.h" +#include "keepkey/firmware/ethereum.h" +#include "keepkey/firmware/ethereum_tokens.h" +#include "keepkey/firmware/mayachain.h" +#include "keepkey/firmware/osmosis.h" +#include "keepkey/firmware/ripple.h" +#include "keepkey/firmware/signtx_tendermint.h" +#include "keepkey/firmware/tendermint.h" +#include "keepkey/firmware/thorchain.h" + #include "messages-ethereum.pb.h" #include "messages-binance.pb.h" #include "messages-cosmos.pb.h" @@ -84,8 +90,7 @@ #include "messages-ripple.pb.h" #include "messages-thorchain.pb.h" #include "messages-mayachain.pb.h" - -#include +#endif // BITCOIN_ONLY #define _(X) (X) @@ -272,10 +277,11 @@ void fsm_msgClearSession(ClearSession *msg) { #include "fsm_msg_common.h" #include "fsm_msg_coin.h" +#include "fsm_msg_debug.h" +#include "fsm_msg_crypto.h" +#ifndef BITCOIN_ONLY #include "fsm_msg_ethereum.h" #include "fsm_msg_nano.h" -#include "fsm_msg_crypto.h" -#include "fsm_msg_debug.h" #include "fsm_msg_eos.h" #include "fsm_msg_cosmos.h" #include "fsm_msg_osmosis.h" @@ -284,3 +290,4 @@ void fsm_msgClearSession(ClearSession *msg) { #include "fsm_msg_tendermint.h" #include "fsm_msg_thorchain.h" #include "fsm_msg_mayachain.h" +#endif // BITCOIN_ONLY \ No newline at end of file diff --git a/lib/firmware/fsm_msg_common.h b/lib/firmware/fsm_msg_common.h index 33cd3e10d..5bf1a6747 100644 --- a/lib/firmware/fsm_msg_common.h +++ b/lib/firmware/fsm_msg_common.h @@ -2,9 +2,11 @@ void fsm_msgInitialize(Initialize *msg) { (void)msg; recovery_cipher_abort(); signing_abort(); +#ifndef BITCOIN_ONLY ethereum_signing_abort(); tendermint_signAbort(); eos_signingAbort(); +#endif session_clear(false); // do not clear PIN layoutHome(); fsm_msgGetFeatures(0); @@ -149,9 +151,12 @@ void fsm_msgGetCoinTable(GetCoinTable *msg) { for (size_t i = 0; i < msg->end - msg->start; i++) { if (msg->start + i < COINS_COUNT) { resp->table[i] = coins[msg->start + i]; - } else if (msg->start + i - COINS_COUNT < TOKENS_COUNT) { + } +#ifndef BITCOIN_ONLY + else if (msg->start + i - COINS_COUNT < TOKENS_COUNT) { coinFromToken(&resp->table[i], &tokens[msg->start + i - COINS_COUNT]); } +#endif // BITCOIN_ONLY } } @@ -545,9 +550,11 @@ void fsm_msgCancel(Cancel *msg) { (void)msg; recovery_cipher_abort(); signing_abort(); +#ifndef BITCOIN_ONLY ethereum_signing_abort(); tendermint_signAbort(); eos_signingAbort(); +#endif // BITCOIN_ONLY fsm_sendFailure(FailureType_Failure_ActionCancelled, "Aborted"); } diff --git a/lib/firmware/mayachain.c b/lib/firmware/mayachain.c index e0b8a08ee..f387617b7 100644 --- a/lib/firmware/mayachain.c +++ b/lib/firmware/mayachain.c @@ -23,10 +23,10 @@ #include "keepkey/firmware/home_sm.h" #include "keepkey/firmware/storage.h" #include "keepkey/firmware/tendermint.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/segwit_addr.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/segwit_addr.h" #include #include diff --git a/lib/firmware/messagemap.def b/lib/firmware/messagemap.def index e8e374386..c830ca4ad 100644 --- a/lib/firmware/messagemap.def +++ b/lib/firmware/messagemap.def @@ -33,6 +33,7 @@ MSG_IN(MessageType_MessageType_RecoveryDevice, RecoveryDevice, fsm_msgRecoveryDevice) MSG_IN(MessageType_MessageType_CharacterAck, CharacterAck, fsm_msgCharacterAck) MSG_IN(MessageType_MessageType_ApplyPolicies, ApplyPolicies, fsm_msgApplyPolicies) +#ifndef BITCOIN_ONLY MSG_IN(MessageType_MessageType_EthereumGetAddress, EthereumGetAddress, fsm_msgEthereumGetAddress) MSG_IN(MessageType_MessageType_EthereumSignTx, EthereumSignTx, fsm_msgEthereumSignTx) MSG_IN(MessageType_MessageType_EthereumTxAck, EthereumTxAck, fsm_msgEthereumTxAck) @@ -72,7 +73,7 @@ MSG_IN(MessageType_MessageType_MayachainGetAddress, MayachainGetAddress, fsm_msgMayachainGetAddress) MSG_IN(MessageType_MessageType_MayachainSignTx, MayachainSignTx, fsm_msgMayachainSignTx) MSG_IN(MessageType_MessageType_MayachainMsgAck, MayachainMsgAck, fsm_msgMayachainMsgAck) - +#endif // BITCOIN_ONLY /* Normal Out Messages */ MSG_OUT(MessageType_MessageType_Success, Success, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_Failure, Failure, NO_PROCESS_FUNC) @@ -95,6 +96,7 @@ MSG_OUT(MessageType_MessageType_PassphraseRequest, PassphraseRequest, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_WordRequest, WordRequest, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_CharacterRequest, CharacterRequest, NO_PROCESS_FUNC) +#ifndef BITCOIN_ONLY MSG_OUT(MessageType_MessageType_EthereumAddress, EthereumAddress, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_EthereumTxRequest, EthereumTxRequest, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_EthereumMessageSignature, EthereumMessageSignature, NO_PROCESS_FUNC) @@ -131,7 +133,7 @@ MSG_OUT(MessageType_MessageType_MayachainAddress, MayachainAddress, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_MayachainMsgRequest, MayachainMsgRequest, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_MayachainSignedTx, MayachainSignedTx, NO_PROCESS_FUNC) - +#endif // BITCOIN_ONLY #if DEBUG_LINK /* Debug Messages */ DEBUG_IN(MessageType_MessageType_DebugLinkDecision, DebugLinkDecision, NO_PROCESS_FUNC) diff --git a/lib/firmware/nano.c b/lib/firmware/nano.c index 9d18dafbd..ec570f87d 100644 --- a/lib/firmware/nano.c +++ b/lib/firmware/nano.c @@ -5,8 +5,8 @@ #include "keepkey/firmware/ethereum.h" #include "keepkey/firmware/fsm.h" #include "keepkey/firmware/home_sm.h" -#include "trezor/crypto/blake2b.h" -#include "trezor/crypto/memzero.h" +#include "hwcrypto/crypto/blake2b.h" +#include "hwcrypto/crypto/memzero.h" #include #include diff --git a/lib/firmware/osmosis.c b/lib/firmware/osmosis.c index a9ac61037..adf50117c 100644 --- a/lib/firmware/osmosis.c +++ b/lib/firmware/osmosis.c @@ -23,10 +23,10 @@ #include "keepkey/firmware/home_sm.h" #include "keepkey/firmware/storage.h" #include "keepkey/firmware/tendermint.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/segwit_addr.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/segwit_addr.h" #include #include diff --git a/lib/firmware/pin_sm.c b/lib/firmware/pin_sm.c index bfd91c8dd..2f16f4d55 100644 --- a/lib/firmware/pin_sm.c +++ b/lib/firmware/pin_sm.c @@ -26,7 +26,7 @@ #include "keepkey/firmware/pin_sm.h" #include "keepkey/firmware/storage.h" #include "keepkey/rand/rng.h" -#include "trezor/crypto/memzero.h" +#include "hwcrypto/crypto/memzero.h" #include #include diff --git a/lib/firmware/recovery_cipher.c b/lib/firmware/recovery_cipher.c index 9677db5c6..966306937 100644 --- a/lib/firmware/recovery_cipher.c +++ b/lib/firmware/recovery_cipher.c @@ -30,9 +30,9 @@ #include "keepkey/firmware/pin_sm.h" #include "keepkey/firmware/storage.h" #include "keepkey/rand/rng.h" -#include "trezor/crypto/bip39.h" -#include "trezor/crypto/bip39_english.h" -#include "trezor/crypto/memzero.h" +#include "hwcrypto/crypto/bip39.h" +#include "hwcrypto/crypto/bip39_english.h" +#include "hwcrypto/crypto/memzero.h" #include #include diff --git a/lib/firmware/reset.c b/lib/firmware/reset.c index b9317ead5..bab925bff 100644 --- a/lib/firmware/reset.c +++ b/lib/firmware/reset.c @@ -28,10 +28,10 @@ #include "keepkey/firmware/storage.h" #include "keepkey/rand/rng.h" #include "keepkey/transport/interface.h" -#include "trezor/crypto/bip39.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/rand.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/bip39.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/rand.h" +#include "hwcrypto/crypto/sha2.h" #include diff --git a/lib/firmware/ripple.c b/lib/firmware/ripple.c index f1c5d60c6..d31d35f9d 100644 --- a/lib/firmware/ripple.c +++ b/lib/firmware/ripple.c @@ -20,8 +20,8 @@ #include "keepkey/firmware/ripple.h" #include "keepkey/firmware/ripple_base58.h" -#include "trezor/crypto/base58.h" -#include "trezor/crypto/secp256k1.h" +#include "hwcrypto/crypto/base58.h" +#include "hwcrypto/crypto/secp256k1.h" #include @@ -39,7 +39,7 @@ const RippleFieldMapping RFM_lastLedgerSequence = {.type = RFT_INT32, const RippleFieldMapping RFM_destinationTag = {.type = RFT_INT32, .key = 14}; bool ripple_getAddress(const uint8_t public_key[33], - char address[MAX_ADDR_SIZE]) { + char address[MAX_RIPPLE_ADDR_SIZE]) { uint8_t buff[64]; memset(buff, 0, sizeof(buff)); @@ -48,7 +48,7 @@ bool ripple_getAddress(const uint8_t public_key[33], hasher_Update(&hasher, public_key, 33); hasher_Final(&hasher, buff + 1); - if (!ripple_encode_check(buff, 21, HASHER_SHA2D, address, MAX_ADDR_SIZE)) { + if (!ripple_encode_check(buff, 21, HASHER_SHA2D, address, MAX_RIPPLE_ADDR_SIZE)) { assert(false && "can't encode address"); return false; } diff --git a/lib/firmware/ripple_base58.c b/lib/firmware/ripple_base58.c index d699fa673..f8f45b59b 100644 --- a/lib/firmware/ripple_base58.c +++ b/lib/firmware/ripple_base58.c @@ -23,9 +23,9 @@ #include "keepkey/firmware/ripple_base58.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/ripemd160.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/ripemd160.h" +#include "hwcrypto/crypto/sha2.h" #include #include diff --git a/lib/firmware/signing.c b/lib/firmware/signing.c index ccc38c226..3205df239 100644 --- a/lib/firmware/signing.c +++ b/lib/firmware/signing.c @@ -33,9 +33,9 @@ #include "keepkey/firmware/signing.h" #include "keepkey/firmware/txin_check.h" #include "keepkey/firmware/transaction.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/secp256k1.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/secp256k1.h" #include "types.pb.h" diff --git a/lib/firmware/signtx_tendermint.c b/lib/firmware/signtx_tendermint.c index 8b87ebdc9..871ccd0bf 100644 --- a/lib/firmware/signtx_tendermint.c +++ b/lib/firmware/signtx_tendermint.c @@ -24,10 +24,10 @@ #include "keepkey/firmware/signtx_tendermint.h" #include "keepkey/firmware/storage.h" #include "keepkey/firmware/tendermint.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/segwit_addr.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/segwit_addr.h" #include #include diff --git a/lib/firmware/storage.c b/lib/firmware/storage.c index 211493ac9..f259205a3 100644 --- a/lib/firmware/storage.c +++ b/lib/firmware/storage.c @@ -48,13 +48,13 @@ #include "keepkey/firmware/u2f.h" #include "keepkey/rand/rng.h" #include "keepkey/transport/interface.h" -#include "trezor/crypto/aes/aes.h" -#include "trezor/crypto/bip32.h" -#include "trezor/crypto/bip39.h" -#include "trezor/crypto/curves.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/pbkdf2.h" -#include "trezor/crypto/rand.h" +#include "hwcrypto/crypto/aes/aes.h" +#include "hwcrypto/crypto/bip32.h" +#include "hwcrypto/crypto/bip39.h" +#include "hwcrypto/crypto/curves.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/pbkdf2.h" +#include "hwcrypto/crypto/rand.h" #include #include diff --git a/lib/firmware/tendermint.c b/lib/firmware/tendermint.c index 01fe86503..e88ad1485 100644 --- a/lib/firmware/tendermint.c +++ b/lib/firmware/tendermint.c @@ -1,8 +1,8 @@ #include "keepkey/firmware/tendermint.h" #include "keepkey/firmware/fsm.h" -#include "trezor/crypto/segwit_addr.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/segwit_addr.h" +#include "hwcrypto/crypto/sha2.h" #include #include diff --git a/lib/firmware/thorchain.c b/lib/firmware/thorchain.c index 76d5730d8..f6101f953 100644 --- a/lib/firmware/thorchain.c +++ b/lib/firmware/thorchain.c @@ -23,10 +23,10 @@ #include "keepkey/firmware/home_sm.h" #include "keepkey/firmware/storage.h" #include "keepkey/firmware/tendermint.h" -#include "trezor/crypto/secp256k1.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/segwit_addr.h" +#include "hwcrypto/crypto/secp256k1.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/segwit_addr.h" #include #include diff --git a/lib/firmware/transaction.c b/lib/firmware/transaction.c index aa37285e0..36dff2825 100644 --- a/lib/firmware/transaction.c +++ b/lib/firmware/transaction.c @@ -30,13 +30,13 @@ #include "keepkey/firmware/thorchain.h" #include "keepkey/firmware/txin_check.h" #include "keepkey/transport/interface.h" -#include "trezor/crypto/address.h" -#include "trezor/crypto/base58.h" -#include "trezor/crypto/cash_addr.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/ripemd160.h" -#include "trezor/crypto/segwit_addr.h" +#include "hwcrypto/crypto/address.h" +#include "hwcrypto/crypto/base58.h" +#include "hwcrypto/crypto/cash_addr.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/ripemd160.h" +#include "hwcrypto/crypto/segwit_addr.h" #include @@ -236,7 +236,9 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, in->op_return_data.size)) { return -1; // user aborted } - } else { + } +#ifndef BITCOIN_ONLY + else { // is this thorchain data? if (!thorchain_parseConfirmMemo((const char *)in->op_return_data.bytes, (size_t)in->op_return_data.size)) { if (!confirm_data(ButtonRequestType_ButtonRequest_ConfirmOutput, @@ -246,6 +248,18 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, } } } + +#else // for btc-only, don't do a thorchain memo check + else { + if (!confirm_data(ButtonRequestType_ButtonRequest_ConfirmOutput, + _("Confirm OP_RETURN"), in->op_return_data.bytes, + in->op_return_data.size)) { + return -1; // user aborted + } + } + +#endif // BITCOIN_ONLY + } uint32_t r = 0; out->script_pubkey.bytes[0] = 0x6A; diff --git a/lib/firmware/txin_check.c b/lib/firmware/txin_check.c index 1a9ac976e..090104552 100644 --- a/lib/firmware/txin_check.c +++ b/lib/firmware/txin_check.c @@ -23,8 +23,8 @@ #include "keepkey/board/layout.h" #include "keepkey/board/util.h" #include "keepkey/firmware/txin_check.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/sha2.h" static uint8_t txin_current_digest[SHA256_DIGEST_LENGTH]; /* current tx txins digest */ diff --git a/lib/firmware/u2f.c b/lib/firmware/u2f.c index cac0db6e5..e91d2ae26 100644 --- a/lib/firmware/u2f.c +++ b/lib/firmware/u2f.c @@ -35,13 +35,13 @@ #include "keepkey/firmware/storage.h" #include "keepkey/firmware/u2f/u2f.h" #include "keepkey/firmware/u2f/u2f_keys.h" -#include "trezor/crypto/bip39.h" -#include "trezor/crypto/bip39_english.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/hmac.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/nist256p1.h" -#include "trezor/crypto/rand.h" +#include "hwcrypto/crypto/bip39.h" +#include "hwcrypto/crypto/bip39_english.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/hmac.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/nist256p1.h" +#include "hwcrypto/crypto/rand.h" #include #include diff --git a/lib/rand/rng.c b/lib/rand/rng.c index 57c8c75b9..1834b7463 100644 --- a/lib/rand/rng.c +++ b/lib/rand/rng.c @@ -19,13 +19,17 @@ #include "keepkey/rand/rng.h" -#include "trezor/crypto/rand.h" +#include "hwcrypto/crypto/rand.h" #ifndef EMULATOR #include #include +#ifdef DEV_DEBUG +#include +#else #include #endif +#endif void reset_rng(void) { #ifndef EMULATOR diff --git a/lib/transport/CMakeLists.txt b/lib/transport/CMakeLists.txt index 41b8d5864..98d76230a 100644 --- a/lib/transport/CMakeLists.txt +++ b/lib/transport/CMakeLists.txt @@ -3,152 +3,195 @@ set(sources pb_encode.c pb_decode.c) -set(protoc_pb_sources + # always build bitcoin messages + set(protoc_pb_sources ${DEVICE_PROTOCOL}/types.proto - ${DEVICE_PROTOCOL}/messages-ethereum.proto - ${DEVICE_PROTOCOL}/messages-eos.proto - ${DEVICE_PROTOCOL}/messages-nano.proto - ${DEVICE_PROTOCOL}/messages-binance.proto - ${DEVICE_PROTOCOL}/messages-cosmos.proto - ${DEVICE_PROTOCOL}/messages-osmosis.proto - ${DEVICE_PROTOCOL}/messages-ripple.proto - ${DEVICE_PROTOCOL}/messages-tendermint.proto - ${DEVICE_PROTOCOL}/messages-thorchain.proto - ${DEVICE_PROTOCOL}/messages-mayachain.proto ${DEVICE_PROTOCOL}/messages.proto) set(protoc_pb_options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/types.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-ethereum.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-eos.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-nano.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-binance.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-cosmos.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-osmosis.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-ripple.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-tendermint.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-thorchain.options - ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-mayachain.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages.options) set(protoc_c_sources ${CMAKE_BINARY_DIR}/lib/transport/types.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-ethereum.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-eos.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-nano.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-binance.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-cosmos.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-osmosis.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-ripple.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.pb.c - ${CMAKE_BINARY_DIR}/lib/transport/messages-mayachain.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages.pb.c) set(protoc_c_headers ${CMAKE_BINARY_DIR}/include/types.pb.h - ${CMAKE_BINARY_DIR}/include/messages-ethereum.pb.h - ${CMAKE_BINARY_DIR}/include/messages-eos.pb.h - ${CMAKE_BINARY_DIR}/include/messages-nano.pb.h - ${CMAKE_BINARY_DIR}/include/messages-binance.pb.h - ${CMAKE_BINARY_DIR}/include/messages-cosmos.pb.h - ${CMAKE_BINARY_DIR}/include/messages-osmosis.pb.h - ${CMAKE_BINARY_DIR}/include/messages-ripple.pb.h - ${CMAKE_BINARY_DIR}/include/messages-tendermint.pb.h - ${CMAKE_BINARY_DIR}/include/messages-thorchain.pb.h - ${CMAKE_BINARY_DIR}/include/messages-mayachain.pb.h ${CMAKE_BINARY_DIR}/include/messages.pb.h) set(protoc_pb_sources_moved ${CMAKE_BINARY_DIR}/lib/transport/types.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-ethereum.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-eos.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-nano.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-binance.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-cosmos.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-osmosis.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-ripple.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.proto - ${CMAKE_BINARY_DIR}/lib/transport/messages-mayachain.proto ${CMAKE_BINARY_DIR}/lib/transport/messages.proto) -add_custom_command( - OUTPUT - ${CMAKE_BINARY_DIR}/lib/transport/kktransport.pb.stamp - ${protoc_c_sources} - ${protoc_c_headers} - WORKING_DIRECTORY - ${CMAKE_BINARY_DIR}/lib/transport - COMMAND - ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/keepkey/transport - COMMAND - ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/lib/transport - COMMAND - ${CMAKE_COMMAND} -E copy ${protoc_pb_options} ${CMAKE_BINARY_DIR}/lib/transport - COMMAND - ${CMAKE_COMMAND} -E copy ${protoc_pb_sources} ${CMAKE_BINARY_DIR}/lib/transport - COMMAND - ${CMAKE_COMMAND} -E copy - ${DEVICE_PROTOCOL}/google/protobuf/descriptor.proto - ${CMAKE_BINARY_DIR}/lib/transport/google/protobuf/descriptor.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f types.options:." types.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-ethereum.options:." messages-ethereum.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-eos.options:." messages-eos.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-nano.options:." messages-nano.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-binance.options:." messages-binance.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-cosmos.options:." messages-cosmos.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-osmosis.options:." messages-osmosis.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-ripple.options:." messages-ripple.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-tendermint.options:." messages-tendermint.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-thorchain.options:." messages-thorchain.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages-mayachain.options:." messages-mayachain.proto - COMMAND - ${PROTOC_BINARY} -I. -I/usr/include - --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb - "--nanopb_out=-f messages.options:." messages.proto - COMMAND - ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/lib/transport/*.pb.h - ${CMAKE_BINARY_DIR}/include - COMMAND - sh -c "! grep -r pb_callback_t ${CMAKE_BINARY_DIR}/include/*.pb.h" || (echo "pb_callback_t forbidden. missing .options entry?" && false) - COMMAND - ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/lib/transport/kktransport.pb.stamp - DEPENDS - ${protoc_pb_sources} ${protoc_pb_options}) +if("${COIN_SUPPORT}" STREQUAL "BTC") +else() + # if full coin support, build rest of messages + list(APPEND protoc_pb_sources + ${DEVICE_PROTOCOL}/messages-ethereum.proto + ${DEVICE_PROTOCOL}/messages-eos.proto + ${DEVICE_PROTOCOL}/messages-nano.proto + ${DEVICE_PROTOCOL}/messages-binance.proto + ${DEVICE_PROTOCOL}/messages-cosmos.proto + ${DEVICE_PROTOCOL}/messages-osmosis.proto + ${DEVICE_PROTOCOL}/messages-ripple.proto + ${DEVICE_PROTOCOL}/messages-tendermint.proto + ${DEVICE_PROTOCOL}/messages-thorchain.proto + ${DEVICE_PROTOCOL}/messages-mayachain.proto) + + list(APPEND protoc_pb_options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-ethereum.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-eos.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-nano.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-binance.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-cosmos.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-osmosis.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-ripple.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-tendermint.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-thorchain.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-mayachain.options) + + list(APPEND protoc_c_sources + ${CMAKE_BINARY_DIR}/lib/transport/messages-ethereum.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-eos.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-nano.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-binance.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-cosmos.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-osmosis.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-ripple.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-mayachain.pb.c) + + list(APPEND protoc_c_headers + ${CMAKE_BINARY_DIR}/include/messages-ethereum.pb.h + ${CMAKE_BINARY_DIR}/include/messages-eos.pb.h + ${CMAKE_BINARY_DIR}/include/messages-nano.pb.h + ${CMAKE_BINARY_DIR}/include/messages-binance.pb.h + ${CMAKE_BINARY_DIR}/include/messages-cosmos.pb.h + ${CMAKE_BINARY_DIR}/include/messages-osmosis.pb.h + ${CMAKE_BINARY_DIR}/include/messages-ripple.pb.h + ${CMAKE_BINARY_DIR}/include/messages-tendermint.pb.h + ${CMAKE_BINARY_DIR}/include/messages-thorchain.pb.h + ${CMAKE_BINARY_DIR}/include/messages-mayachain.pb.h) + + list(APPEND protoc_pb_sources_moved + ${CMAKE_BINARY_DIR}/lib/transport/messages-ethereum.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-eos.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-nano.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-binance.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-cosmos.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-osmosis.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-ripple.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-mayachain.proto) +endif() + +if("${COIN_SUPPORT}" STREQUAL "BTC") + # build bitcoin only messages + add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/lib/transport/kktransport.pb.stamp + ${protoc_c_sources} + ${protoc_c_headers} + WORKING_DIRECTORY + ${CMAKE_BINARY_DIR}/lib/transport + COMMAND + ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/keepkey/transport + COMMAND + ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/lib/transport + COMMAND + ${CMAKE_COMMAND} -E copy ${protoc_pb_options} ${CMAKE_BINARY_DIR}/lib/transport + COMMAND + ${CMAKE_COMMAND} -E copy ${protoc_pb_sources} ${CMAKE_BINARY_DIR}/lib/transport + COMMAND + ${CMAKE_COMMAND} -E copy + ${DEVICE_PROTOCOL}/google/protobuf/descriptor.proto + ${CMAKE_BINARY_DIR}/lib/transport/google/protobuf/descriptor.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. types.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages.proto + COMMAND + ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/lib/transport/*.pb.h + ${CMAKE_BINARY_DIR}/include + COMMAND + sh -c "! grep -r pb_callback_t ${CMAKE_BINARY_DIR}/include/*.pb.h" || (echo "pb_callback_t forbidden. missing .options entry?" && false) + COMMAND + ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/lib/transport/kktransport.pb.stamp + DEPENDS + ${protoc_pb_sources} ${protoc_pb_options}) + +else() + # if full coin support, build rest of messages + add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/lib/transport/kktransport.pb.stamp + ${protoc_c_sources} + ${protoc_c_headers} + WORKING_DIRECTORY + ${CMAKE_BINARY_DIR}/lib/transport + COMMAND + ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/keepkey/transport + COMMAND + ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/lib/transport + COMMAND + ${CMAKE_COMMAND} -E copy ${protoc_pb_options} ${CMAKE_BINARY_DIR}/lib/transport + COMMAND + ${CMAKE_COMMAND} -E copy ${protoc_pb_sources} ${CMAKE_BINARY_DIR}/lib/transport + COMMAND + ${CMAKE_COMMAND} -E copy + ${DEVICE_PROTOCOL}/google/protobuf/descriptor.proto + ${CMAKE_BINARY_DIR}/lib/transport/google/protobuf/descriptor.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. types.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-ethereum.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-eos.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-nano.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-binance.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-cosmos.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-osmosis.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-ripple.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-tendermint.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-thorchain.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages-mayachain.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb --nanopb_out=. messages.proto + COMMAND + ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/lib/transport/*.pb.h + ${CMAKE_BINARY_DIR}/include + COMMAND + sh -c "! grep -r pb_callback_t ${CMAKE_BINARY_DIR}/include/*.pb.h" || (echo "pb_callback_t forbidden. missing .options entry?" && false) + COMMAND + ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/lib/transport/kktransport.pb.stamp + DEPENDS + ${protoc_pb_sources} ${protoc_pb_options}) +endif() add_custom_target(kktransport.pb ALL DEPENDS ${CMAKE_BINARY_DIR}/lib/transport/kktransport.pb.stamp) diff --git a/lib/transport/pb_common.c b/lib/transport/pb_common.c deleted file mode 100644 index 5b12eb005..000000000 --- a/lib/transport/pb_common.c +++ /dev/null @@ -1,83 +0,0 @@ -/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. - * - * 2014 Petteri Aimonen - */ - -#include "pb_common.h" - -bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_field_t *fields, - void *dest_struct) { - iter->start = fields; - iter->pos = fields; - iter->required_field_index = 0; - iter->dest_struct = dest_struct; - iter->pData = (char *)dest_struct + iter->pos->data_offset; - iter->pSize = (char *)iter->pData + iter->pos->size_offset; - - return (iter->pos->tag != 0); -} - -bool pb_field_iter_next(pb_field_iter_t *iter) { - const pb_field_t *prev_field = iter->pos; - - if (prev_field->tag == 0) { - /* Handle empty message types, where the first field is already the - * terminator. - * In other cases, the iter->pos never points to the terminator. */ - return false; - } - - iter->pos++; - - if (iter->pos->tag == 0) { - /* Wrapped back to beginning, reinitialize */ - (void)pb_field_iter_begin(iter, iter->start, iter->dest_struct); - return false; - } else { - /* Increment the pointers based on previous field size */ - size_t prev_size = prev_field->data_size; - - if (PB_HTYPE(prev_field->type) == PB_HTYPE_ONEOF && - PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF && - iter->pos->data_offset == PB_SIZE_MAX) { - /* Don't advance pointers inside unions */ - return true; - } else if (PB_ATYPE(prev_field->type) == PB_ATYPE_STATIC && - PB_HTYPE(prev_field->type) == PB_HTYPE_REPEATED) { - /* In static arrays, the data_size tells the size of a single entry and - * array_size is the number of entries */ - prev_size *= prev_field->array_size; - } else if (PB_ATYPE(prev_field->type) == PB_ATYPE_POINTER) { - /* Pointer fields always have a constant size in the main structure. - * The data_size only applies to the dynamically allocated area. */ - prev_size = sizeof(void *); - } - - if (PB_HTYPE(prev_field->type) == PB_HTYPE_REQUIRED) { - /* Count the required fields, in order to check their presence in the - * decoder. */ - iter->required_field_index++; - } - - iter->pData = (char *)iter->pData + prev_size + iter->pos->data_offset; - iter->pSize = (char *)iter->pData + iter->pos->size_offset; - return true; - } -} - -bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) { - const pb_field_t *start = iter->pos; - - do { - if (iter->pos->tag == tag && - PB_LTYPE(iter->pos->type) != PB_LTYPE_EXTENSION) { - /* Found the wanted field */ - return true; - } - - (void)pb_field_iter_next(iter); - } while (iter->pos != start); - - /* Searched all the way back to start, and found nothing. */ - return false; -} diff --git a/lib/transport/pb_decode.c b/lib/transport/pb_decode.c deleted file mode 100644 index 5ff4ecd7e..000000000 --- a/lib/transport/pb_decode.c +++ /dev/null @@ -1,1397 +0,0 @@ -/* pb_decode.c -- decode a protobuf using minimal resources - * - * 2011 Petteri Aimonen - */ - -/* Use the GCC warn_unused_result attribute to check that all return values - * are propagated correctly. On other compilers and gcc before 3.4.0 just - * ignore the annotation. - */ -#if !defined(__GNUC__) || (__GNUC__ < 3) || \ - (__GNUC__ == 3 && __GNUC_MINOR__ < 4) -#define checkreturn -#else -#define checkreturn __attribute__((warn_unused_result)) -#endif - -#include "pb.h" -#include "pb_decode.h" -#include "pb_common.h" - -/************************************** - * Declarations internal to this file * - **************************************/ - -typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, - void *dest) checkreturn; - -static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, - size_t count); -static bool checkreturn read_raw_value(pb_istream_t *stream, - pb_wire_type_t wire_type, pb_byte_t *buf, - size_t *size); -static bool checkreturn decode_static_field(pb_istream_t *stream, - pb_wire_type_t wire_type, - pb_field_iter_t *iter); -static bool checkreturn decode_callback_field(pb_istream_t *stream, - pb_wire_type_t wire_type, - pb_field_iter_t *iter); -static bool checkreturn decode_field(pb_istream_t *stream, - pb_wire_type_t wire_type, - pb_field_iter_t *iter); -static void iter_from_extension(pb_field_iter_t *iter, - pb_extension_t *extension); -static bool checkreturn default_extension_decoder(pb_istream_t *stream, - pb_extension_t *extension, - uint32_t tag, - pb_wire_type_t wire_type); -static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, - pb_wire_type_t wire_type, - pb_field_iter_t *iter); -static bool checkreturn find_extension_field(pb_field_iter_t *iter); -static void pb_field_set_to_default(pb_field_iter_t *iter); -static void pb_message_set_to_defaults(const pb_field_t fields[], - void *dest_struct); -static bool checkreturn pb_dec_bool(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_dec_varint(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, - uint32_t *dest, bool *eof); -static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_dec_svarint(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_dec_bytes(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_dec_string(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_dec_submessage(pb_istream_t *stream, - const pb_field_t *field, void *dest); -static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, - const pb_field_t *field, - void *dest); -static bool checkreturn pb_skip_varint(pb_istream_t *stream); -static bool checkreturn pb_skip_string(pb_istream_t *stream); - -#ifdef PB_ENABLE_MALLOC -static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, - size_t data_size, size_t array_size); -static bool checkreturn pb_release_union_field(pb_istream_t *stream, - pb_field_iter_t *iter); -static void pb_release_single_field(const pb_field_iter_t *iter); -#endif - -#ifdef PB_WITHOUT_64BIT -#define pb_int64_t int32_t -#define pb_uint64_t uint32_t -#else -#define pb_int64_t int64_t -#define pb_uint64_t uint64_t -#endif - -/* --- Function pointers to field decoders --- - * Order in the array must match pb_action_t LTYPE numbering. - */ -static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = { - &pb_dec_bool, - &pb_dec_varint, - &pb_dec_uvarint, - &pb_dec_svarint, - &pb_dec_fixed32, - &pb_dec_fixed64, - - &pb_dec_bytes, - &pb_dec_string, - &pb_dec_submessage, - NULL, /* extensions */ - &pb_dec_fixed_length_bytes}; - -/******************************* - * pb_istream_t implementation * - *******************************/ - -static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, - size_t count) { - size_t i; - const pb_byte_t *source = (const pb_byte_t *)stream->state; - stream->state = (pb_byte_t *)stream->state + count; - - if (buf != NULL) { - for (i = 0; i < count; i++) buf[i] = source[i]; - } - - return true; -} - -bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { - if (count == 0) return true; - -#ifndef PB_BUFFER_ONLY - if (buf == NULL && stream->callback != buf_read) { - /* Skip input bytes */ - pb_byte_t tmp[16]; - while (count > 16) { - if (!pb_read(stream, tmp, 16)) return false; - - count -= 16; - } - - return pb_read(stream, tmp, count); - } -#endif - - if (stream->bytes_left < count) PB_RETURN_ERROR(stream, "end-of-stream"); - -#ifndef PB_BUFFER_ONLY - if (!stream->callback(stream, buf, count)) - PB_RETURN_ERROR(stream, "io error"); -#else - if (!buf_read(stream, buf, count)) return false; -#endif - - stream->bytes_left -= count; - return true; -} - -/* Read a single byte from input stream. buf may not be NULL. - * This is an optimization for the varint decoding. */ -static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) { - if (stream->bytes_left == 0) PB_RETURN_ERROR(stream, "end-of-stream"); - -#ifndef PB_BUFFER_ONLY - if (!stream->callback(stream, buf, 1)) PB_RETURN_ERROR(stream, "io error"); -#else - *buf = *(const pb_byte_t *)stream->state; - stream->state = (pb_byte_t *)stream->state + 1; -#endif - - stream->bytes_left--; - - return true; -} - -pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize) { - pb_istream_t stream; - /* Cast away the const from buf without a compiler error. We are - * careful to use it only in a const manner in the callbacks. - */ - union { - void *state; - const void *c_state; - } state; -#ifdef PB_BUFFER_ONLY - stream.callback = NULL; -#else - stream.callback = &buf_read; -#endif - state.c_state = buf; - stream.state = state.state; - stream.bytes_left = bufsize; -#ifndef PB_NO_ERRMSG - stream.errmsg = NULL; -#endif - return stream; -} - -/******************** - * Helper functions * - ********************/ - -static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, - uint32_t *dest, bool *eof) { - pb_byte_t byte; - uint32_t result; - - if (!pb_readbyte(stream, &byte)) { - if (stream->bytes_left == 0) { - if (eof) { - *eof = true; - } - } - - return false; - } - - if ((byte & 0x80) == 0) { - /* Quick case, 1 byte value */ - result = byte; - } else { - /* Multibyte case */ - uint_fast8_t bitpos = 7; - result = byte & 0x7F; - - do { - if (!pb_readbyte(stream, &byte)) return false; - - if (bitpos >= 32) { - /* Note: The varint could have trailing 0x80 bytes, or 0xFF for - * negative. */ - uint8_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; - - if ((byte & 0x7F) != 0x00 && - ((result >> 31) == 0 || byte != sign_extension)) { - PB_RETURN_ERROR(stream, "varint overflow"); - } - } else { - result |= (uint32_t)(byte & 0x7F) << bitpos; - } - bitpos = (uint_fast8_t)(bitpos + 7); - } while (byte & 0x80); - - if (bitpos == 35 && (byte & 0x70) != 0) { - /* The last byte was at bitpos=28, so only bottom 4 bits fit. */ - PB_RETURN_ERROR(stream, "varint overflow"); - } - } - - *dest = result; - return true; -} - -bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) { - return pb_decode_varint32_eof(stream, dest, NULL); -} - -#ifndef PB_WITHOUT_64BIT -bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) { - pb_byte_t byte; - uint_fast8_t bitpos = 0; - uint64_t result = 0; - - do { - if (bitpos >= 64) PB_RETURN_ERROR(stream, "varint overflow"); - - if (!pb_readbyte(stream, &byte)) return false; - - result |= (uint64_t)(byte & 0x7F) << bitpos; - bitpos = (uint_fast8_t)(bitpos + 7); - } while (byte & 0x80); - - *dest = result; - return true; -} -#endif - -bool checkreturn pb_skip_varint(pb_istream_t *stream) { - pb_byte_t byte; - do { - if (!pb_read(stream, &byte, 1)) return false; - } while (byte & 0x80); - return true; -} - -bool checkreturn pb_skip_string(pb_istream_t *stream) { - uint32_t length; - if (!pb_decode_varint32(stream, &length)) return false; - - return pb_read(stream, NULL, length); -} - -bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, - uint32_t *tag, bool *eof) { - uint32_t temp; - *eof = false; - *wire_type = (pb_wire_type_t)0; - *tag = 0; - - if (!pb_decode_varint32_eof(stream, &temp, eof)) { - return false; - } - - if (temp == 0) { - *eof = true; /* Special feature: allow 0-terminated messages. */ - return false; - } - - *tag = temp >> 3; - *wire_type = (pb_wire_type_t)(temp & 7); - return true; -} - -bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) { - switch (wire_type) { - case PB_WT_VARINT: - return pb_skip_varint(stream); - case PB_WT_64BIT: - return pb_read(stream, NULL, 8); - case PB_WT_STRING: - return pb_skip_string(stream); - case PB_WT_32BIT: - return pb_read(stream, NULL, 4); - default: - PB_RETURN_ERROR(stream, "invalid wire_type"); - } -} - -/* Read a raw value to buffer, for the purpose of passing it to callback as - * a substream. Size is maximum size on call, and actual size on return. - */ -static bool checkreturn read_raw_value(pb_istream_t *stream, - pb_wire_type_t wire_type, pb_byte_t *buf, - size_t *size) { - size_t max_size = *size; - switch (wire_type) { - case PB_WT_VARINT: - *size = 0; - do { - (*size)++; - if (*size > max_size) return false; - if (!pb_read(stream, buf, 1)) return false; - } while (*buf++ & 0x80); - return true; - - case PB_WT_64BIT: - *size = 8; - return pb_read(stream, buf, 8); - - case PB_WT_32BIT: - *size = 4; - return pb_read(stream, buf, 4); - - case PB_WT_STRING: - /* Calling read_raw_value with a PB_WT_STRING is an error. - * Explicitly handle this case and fallthrough to default to avoid - * compiler warnings. - */ - - default: - PB_RETURN_ERROR(stream, "invalid wire_type"); - } -} - -/* Decode string length from stream and return a substream with limited length. - * Remember to close the substream using pb_close_string_substream(). - */ -bool checkreturn pb_make_string_substream(pb_istream_t *stream, - pb_istream_t *substream) { - uint32_t size; - if (!pb_decode_varint32(stream, &size)) return false; - - *substream = *stream; - if (substream->bytes_left < size) - PB_RETURN_ERROR(stream, "parent stream too short"); - - substream->bytes_left = size; - stream->bytes_left -= size; - return true; -} - -bool checkreturn pb_close_string_substream(pb_istream_t *stream, - pb_istream_t *substream) { - if (substream->bytes_left) { - if (!pb_read(substream, NULL, substream->bytes_left)) return false; - } - - stream->state = substream->state; - -#ifndef PB_NO_ERRMSG - stream->errmsg = substream->errmsg; -#endif - return true; -} - -/************************* - * Decode a single field * - *************************/ - -static bool checkreturn decode_static_field(pb_istream_t *stream, - pb_wire_type_t wire_type, - pb_field_iter_t *iter) { - pb_type_t type; - pb_decoder_t func; - - type = iter->pos->type; - func = PB_DECODERS[PB_LTYPE(type)]; - - switch (PB_HTYPE(type)) { - case PB_HTYPE_REQUIRED: - return func(stream, iter->pos, iter->pData); - - case PB_HTYPE_OPTIONAL: - if (iter->pSize != iter->pData) *(bool *)iter->pSize = true; - return func(stream, iter->pos, iter->pData); - - case PB_HTYPE_REPEATED: - if (wire_type == PB_WT_STRING && - PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) { - /* Packed array */ - bool status = true; - pb_size_t *size = (pb_size_t *)iter->pSize; - - pb_istream_t substream; - if (!pb_make_string_substream(stream, &substream)) return false; - - while (substream.bytes_left > 0 && *size < iter->pos->array_size) { - void *pItem = (char *)iter->pData + iter->pos->data_size * (*size); - if (!func(&substream, iter->pos, pItem)) { - status = false; - break; - } - (*size)++; - } - - if (substream.bytes_left != 0) - PB_RETURN_ERROR(stream, "array overflow"); - if (!pb_close_string_substream(stream, &substream)) return false; - - return status; - } else { - /* Repeated field */ - pb_size_t *size = (pb_size_t *)iter->pSize; - char *pItem = (char *)iter->pData + iter->pos->data_size * (*size); - - if ((*size)++ >= iter->pos->array_size) - PB_RETURN_ERROR(stream, "array overflow"); - - return func(stream, iter->pos, pItem); - } - - case PB_HTYPE_ONEOF: - *(pb_size_t *)iter->pSize = iter->pos->tag; - if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) { - /* We memset to zero so that any callbacks are set to NULL. - * Then set any default values. */ - memset(iter->pData, 0, iter->pos->data_size); - pb_message_set_to_defaults((const pb_field_t *)iter->pos->ptr, - iter->pData); - } - return func(stream, iter->pos, iter->pData); - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -} - -#ifdef PB_ENABLE_MALLOC -/* Allocate storage for the field and store the pointer at iter->pData. - * array_size is the number of entries to reserve in an array. - * Zero size is not allowed, use pb_free() for releasing. - */ -static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, - size_t data_size, size_t array_size) { - void *ptr = *(void **)pData; - - if (data_size == 0 || array_size == 0) - PB_RETURN_ERROR(stream, "invalid size"); - - /* Check for multiplication overflows. - * This code avoids the costly division if the sizes are small enough. - * Multiplication is safe as long as only half of bits are set - * in either multiplicand. - */ - { - const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); - if (data_size >= check_limit || array_size >= check_limit) { - const size_t size_max = (size_t)-1; - if (size_max / array_size < data_size) { - PB_RETURN_ERROR(stream, "size too large"); - } - } - } - - /* Allocate new or expand previous allocation */ - /* Note: on failure the old pointer will remain in the structure, - * the message must be freed by caller also on error return. */ - ptr = pb_realloc(ptr, array_size * data_size); - if (ptr == NULL) PB_RETURN_ERROR(stream, "realloc failed"); - - *(void **)pData = ptr; - return true; -} - -/* Clear a newly allocated item in case it contains a pointer, or is a - * submessage. */ -static void initialize_pointer_field(void *pItem, pb_field_iter_t *iter) { - if (PB_LTYPE(iter->pos->type) == PB_LTYPE_STRING || - PB_LTYPE(iter->pos->type) == PB_LTYPE_BYTES) { - *(void **)pItem = NULL; - } else if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE) { - /* We memset to zero so that any callbacks are set to NULL. - * Then set any default values. */ - memset(pItem, 0, iter->pos->data_size); - pb_message_set_to_defaults((const pb_field_t *)iter->pos->ptr, pItem); - } -} -#endif - -static bool checkreturn decode_pointer_field(pb_istream_t *stream, - pb_wire_type_t wire_type, - pb_field_iter_t *iter) { -#ifndef PB_ENABLE_MALLOC - PB_UNUSED(wire_type); - PB_UNUSED(iter); - PB_RETURN_ERROR(stream, "no malloc support"); -#else - pb_type_t type; - pb_decoder_t func; - - type = iter->pos->type; - func = PB_DECODERS[PB_LTYPE(type)]; - - switch (PB_HTYPE(type)) { - case PB_HTYPE_REQUIRED: - case PB_HTYPE_OPTIONAL: - case PB_HTYPE_ONEOF: - if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && - *(void **)iter->pData != NULL) { - /* Duplicate field, have to release the old allocation first. */ - pb_release_single_field(iter); - } - - if (PB_HTYPE(type) == PB_HTYPE_ONEOF) { - *(pb_size_t *)iter->pSize = iter->pos->tag; - } - - if (PB_LTYPE(type) == PB_LTYPE_STRING || - PB_LTYPE(type) == PB_LTYPE_BYTES) { - return func(stream, iter->pos, iter->pData); - } else { - if (!allocate_field(stream, iter->pData, iter->pos->data_size, 1)) - return false; - - initialize_pointer_field(*(void **)iter->pData, iter); - return func(stream, iter->pos, *(void **)iter->pData); - } - - case PB_HTYPE_REPEATED: - if (wire_type == PB_WT_STRING && - PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) { - /* Packed array, multiple items come in at once. */ - bool status = true; - pb_size_t *size = (pb_size_t *)iter->pSize; - size_t allocated_size = *size; - void *pItem; - pb_istream_t substream; - - if (!pb_make_string_substream(stream, &substream)) return false; - - while (substream.bytes_left) { - if ((size_t)*size + 1 > allocated_size) { - /* Allocate more storage. This tries to guess the - * number of remaining entries. Round the division - * upwards. */ - allocated_size += - (substream.bytes_left - 1) / iter->pos->data_size + 1; - - if (!allocate_field(&substream, iter->pData, iter->pos->data_size, - allocated_size)) { - status = false; - break; - } - } - - /* Decode the array entry */ - pItem = *(char **)iter->pData + iter->pos->data_size * (*size); - initialize_pointer_field(pItem, iter); - if (!func(&substream, iter->pos, pItem)) { - status = false; - break; - } - - if (*size == PB_SIZE_MAX) { -#ifndef PB_NO_ERRMSG - stream->errmsg = "too many array entries"; -#endif - status = false; - break; - } - - (*size)++; - } - if (!pb_close_string_substream(stream, &substream)) return false; - - return status; - } else { - /* Normal repeated field, i.e. only one item at a time. */ - pb_size_t *size = (pb_size_t *)iter->pSize; - void *pItem; - - if (*size == PB_SIZE_MAX) - PB_RETURN_ERROR(stream, "too many array entries"); - - (*size)++; - if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size)) - return false; - - pItem = *(char **)iter->pData + iter->pos->data_size * (*size - 1); - initialize_pointer_field(pItem, iter); - return func(stream, iter->pos, pItem); - } - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -#endif -} - -static bool checkreturn decode_callback_field(pb_istream_t *stream, - pb_wire_type_t wire_type, - pb_field_iter_t *iter) { - pb_callback_t *pCallback = (pb_callback_t *)iter->pData; -#ifdef PB_OLD_CALLBACK_STYLE - void *arg; -#else - void **arg; -#endif - - if (pCallback == NULL || pCallback->funcs.decode == NULL) - return pb_skip_field(stream, wire_type); - -#ifdef PB_OLD_CALLBACK_STYLE - arg = pCallback->arg; -#else - arg = &(pCallback->arg); -#endif - - if (wire_type == PB_WT_STRING) { - pb_istream_t substream; - - if (!pb_make_string_substream(stream, &substream)) return false; - - do { - if (!pCallback->funcs.decode(&substream, iter->pos, arg)) - PB_RETURN_ERROR(stream, "callback failed"); - } while (substream.bytes_left); - - if (!pb_close_string_substream(stream, &substream)) return false; - - return true; - } else { - /* Copy the single scalar value to stack. - * This is required so that we can limit the stream length, - * which in turn allows to use same callback for packed and - * not-packed fields. */ - pb_istream_t substream; - pb_byte_t buffer[10]; - size_t size = sizeof(buffer); - - if (!read_raw_value(stream, wire_type, buffer, &size)) return false; - substream = pb_istream_from_buffer(buffer, size); - - return pCallback->funcs.decode(&substream, iter->pos, arg); - } -} - -static bool checkreturn decode_field(pb_istream_t *stream, - pb_wire_type_t wire_type, - pb_field_iter_t *iter) { -#ifdef PB_ENABLE_MALLOC - /* When decoding an oneof field, check if there is old data that must be - * released first. */ - if (PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF) { - if (!pb_release_union_field(stream, iter)) return false; - } -#endif - - switch (PB_ATYPE(iter->pos->type)) { - case PB_ATYPE_STATIC: - return decode_static_field(stream, wire_type, iter); - - case PB_ATYPE_POINTER: - return decode_pointer_field(stream, wire_type, iter); - - case PB_ATYPE_CALLBACK: - return decode_callback_field(stream, wire_type, iter); - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -} - -static void iter_from_extension(pb_field_iter_t *iter, - pb_extension_t *extension) { - /* Fake a field iterator for the extension field. - * It is not actually safe to advance this iterator, but decode_field - * will not even try to. */ - const pb_field_t *field = (const pb_field_t *)extension->type->arg; - (void)pb_field_iter_begin(iter, field, extension->dest); - iter->pData = extension->dest; - iter->pSize = &extension->found; - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { - /* For pointer extensions, the pointer is stored directly - * in the extension structure. This avoids having an extra - * indirection. */ - iter->pData = &extension->dest; - } -} - -/* Default handler for extension fields. Expects a pb_field_t structure - * in extension->type->arg. */ -static bool checkreturn default_extension_decoder(pb_istream_t *stream, - pb_extension_t *extension, - uint32_t tag, - pb_wire_type_t wire_type) { - const pb_field_t *field = (const pb_field_t *)extension->type->arg; - pb_field_iter_t iter; - - if (field->tag != tag) return true; - - iter_from_extension(&iter, extension); - extension->found = true; - return decode_field(stream, wire_type, &iter); -} - -/* Try to decode an unknown field as an extension field. Tries each extension - * decoder in turn, until one of them handles the field or loop ends. */ -static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, - pb_wire_type_t wire_type, - pb_field_iter_t *iter) { - pb_extension_t *extension = *(pb_extension_t *const *)iter->pData; - size_t pos = stream->bytes_left; - - while (extension != NULL && pos == stream->bytes_left) { - bool status; - if (extension->type->decode) - status = extension->type->decode(stream, extension, tag, wire_type); - else - status = default_extension_decoder(stream, extension, tag, wire_type); - - if (!status) return false; - - extension = extension->next; - } - - return true; -} - -/* Step through the iterator until an extension field is found or until all - * entries have been checked. There can be only one extension field per - * message. Returns false if no extension field is found. */ -static bool checkreturn find_extension_field(pb_field_iter_t *iter) { - const pb_field_t *start = iter->pos; - - do { - if (PB_LTYPE(iter->pos->type) == PB_LTYPE_EXTENSION) return true; - (void)pb_field_iter_next(iter); - } while (iter->pos != start); - - return false; -} - -/* Initialize message fields to default values, recursively */ -static void pb_field_set_to_default(pb_field_iter_t *iter) { - pb_type_t type; - type = iter->pos->type; - - if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) { - pb_extension_t *ext = *(pb_extension_t *const *)iter->pData; - while (ext != NULL) { - pb_field_iter_t ext_iter; - ext->found = false; - iter_from_extension(&ext_iter, ext); - pb_field_set_to_default(&ext_iter); - ext = ext->next; - } - } else if (PB_ATYPE(type) == PB_ATYPE_STATIC) { - bool init_data = true; - if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && iter->pSize != iter->pData) { - /* Set has_field to false. Still initialize the optional field - * itself also. */ - *(bool *)iter->pSize = false; - } else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || - PB_HTYPE(type) == PB_HTYPE_ONEOF) { - /* REPEATED: Set array count to 0, no need to initialize contents. - ONEOF: Set which_field to 0. */ - *(pb_size_t *)iter->pSize = 0; - init_data = false; - } - - if (init_data) { - if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE) { - /* Initialize submessage to defaults */ - pb_message_set_to_defaults((const pb_field_t *)iter->pos->ptr, - iter->pData); - } else if (iter->pos->ptr != NULL) { - /* Initialize to default value */ - memcpy(iter->pData, iter->pos->ptr, iter->pos->data_size); - } else { - /* Initialize to zeros */ - memset(iter->pData, 0, iter->pos->data_size); - } - } - } else if (PB_ATYPE(type) == PB_ATYPE_POINTER) { - /* Initialize the pointer to NULL. */ - *(void **)iter->pData = NULL; - - /* Initialize array count to 0. */ - if (PB_HTYPE(type) == PB_HTYPE_REPEATED || - PB_HTYPE(type) == PB_HTYPE_ONEOF) { - *(pb_size_t *)iter->pSize = 0; - } - } else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) { - /* Don't overwrite callback */ - } -} - -static void pb_message_set_to_defaults(const pb_field_t fields[], - void *dest_struct) { - pb_field_iter_t iter; - - if (!pb_field_iter_begin(&iter, fields, dest_struct)) - return; /* Empty message type */ - - do { - pb_field_set_to_default(&iter); - } while (pb_field_iter_next(&iter)); -} - -/********************* - * Decode all fields * - *********************/ - -bool checkreturn pb_decode_noinit(pb_istream_t *stream, - const pb_field_t fields[], - void *dest_struct) { - uint32_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 31) / 32] = {0, 0}; - const uint32_t allbits = ~(uint32_t)0; - uint32_t extension_range_start = 0; - pb_field_iter_t iter; - - /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated - * fixed count field. This can only handle _one_ repeated fixed count field - * that is unpacked and unordered among other (non repeated fixed count) - * fields. - */ - const pb_field_t *fixed_count_field = NULL; - pb_size_t fixed_count_size = 0; - - /* Return value ignored, as empty message types will be correctly handled by - * pb_field_iter_find() anyway. */ - (void)pb_field_iter_begin(&iter, fields, dest_struct); - - while (stream->bytes_left) { - uint32_t tag; - pb_wire_type_t wire_type; - bool eof; - - if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) { - if (eof) - break; - else - return false; - } - - if (!pb_field_iter_find(&iter, tag)) { - /* No match found, check if it matches an extension. */ - if (tag >= extension_range_start) { - if (!find_extension_field(&iter)) - extension_range_start = (uint32_t)-1; - else - extension_range_start = iter.pos->tag; - - if (tag >= extension_range_start) { - size_t pos = stream->bytes_left; - - if (!decode_extension(stream, tag, wire_type, &iter)) return false; - - if (pos != stream->bytes_left) { - /* The field was handled */ - continue; - } - } - } - - /* No match found, skip data */ - if (!pb_skip_field(stream, wire_type)) return false; - continue; - } - - /* If a repeated fixed count field was found, get size from - * 'fixed_count_field' as there is no counter contained in the struct. - */ - if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REPEATED && - iter.pSize == iter.pData) { - if (fixed_count_field != iter.pos) { - /* If the new fixed count field does not match the previous one, - * check that the previous one is NULL or that it finished - * receiving all the expected data. - */ - if (fixed_count_field != NULL && - fixed_count_size != fixed_count_field->array_size) { - PB_RETURN_ERROR(stream, "wrong size for fixed count field"); - } - - fixed_count_field = iter.pos; - fixed_count_size = 0; - } - - iter.pSize = &fixed_count_size; - } - - if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REQUIRED && - iter.required_field_index < PB_MAX_REQUIRED_FIELDS) { - uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); - fields_seen[iter.required_field_index >> 5] |= tmp; - } - - if (!decode_field(stream, wire_type, &iter)) return false; - } - - /* Check that all elements of the last decoded fixed count field were present. - */ - if (fixed_count_field != NULL && - fixed_count_size != fixed_count_field->array_size) { - PB_RETURN_ERROR(stream, "wrong size for fixed count field"); - } - - /* Check that all required fields were present. */ - { - /* First figure out the number of required fields by - * seeking to the end of the field array. Usually we - * are already close to end after decoding. - */ - unsigned req_field_count; - pb_type_t last_type; - unsigned i; - do { - req_field_count = iter.required_field_index; - last_type = iter.pos->type; - } while (pb_field_iter_next(&iter)); - - /* Fixup if last field was also required. */ - if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0) - req_field_count++; - - if (req_field_count > PB_MAX_REQUIRED_FIELDS) - req_field_count = PB_MAX_REQUIRED_FIELDS; - - if (req_field_count > 0) { - /* Check the whole words */ - for (i = 0; i < (req_field_count >> 5); i++) { - if (fields_seen[i] != allbits) - PB_RETURN_ERROR(stream, "missing required field"); - } - - /* Check the remaining bits (if any) */ - if ((req_field_count & 31) != 0) { - if (fields_seen[req_field_count >> 5] != - (allbits >> (32 - (req_field_count & 31)))) { - PB_RETURN_ERROR(stream, "missing required field"); - } - } - } - } - - return true; -} - -bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct) { - bool status; - pb_message_set_to_defaults(fields, dest_struct); - status = pb_decode_noinit(stream, fields, dest_struct); - -#ifdef PB_ENABLE_MALLOC - if (!status) pb_release(fields, dest_struct); -#endif - - return status; -} - -bool pb_decode_delimited_noinit(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct) { - pb_istream_t substream; - bool status; - - if (!pb_make_string_substream(stream, &substream)) return false; - - status = pb_decode_noinit(&substream, fields, dest_struct); - - if (!pb_close_string_substream(stream, &substream)) return false; - return status; -} - -bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct) { - pb_istream_t substream; - bool status; - - if (!pb_make_string_substream(stream, &substream)) return false; - - status = pb_decode(&substream, fields, dest_struct); - - if (!pb_close_string_substream(stream, &substream)) return false; - return status; -} - -bool pb_decode_nullterminated(pb_istream_t *stream, const pb_field_t fields[], - void *dest_struct) { - /* This behaviour will be separated in nanopb-0.4.0, see issue #278. */ - return pb_decode(stream, fields, dest_struct); -} - -#ifdef PB_ENABLE_MALLOC -/* Given an oneof field, if there has already been a field inside this oneof, - * release it before overwriting with a different one. */ -static bool pb_release_union_field(pb_istream_t *stream, - pb_field_iter_t *iter) { - pb_size_t old_tag = *(pb_size_t *)iter->pSize; /* Previous which_ value */ - pb_size_t new_tag = iter->pos->tag; /* New which_ value */ - - if (old_tag == 0) return true; /* Ok, no old data in union */ - - if (old_tag == new_tag) - return true; /* Ok, old data is of same type => merge */ - - /* Release old data. The find can fail if the message struct contains - * invalid data. */ - if (!pb_field_iter_find(iter, old_tag)) - PB_RETURN_ERROR(stream, "invalid union tag"); - - pb_release_single_field(iter); - - /* Restore iterator to where it should be. - * This shouldn't fail unless the pb_field_t structure is corrupted. */ - if (!pb_field_iter_find(iter, new_tag)) - PB_RETURN_ERROR(stream, "iterator error"); - - return true; -} - -static void pb_release_single_field(const pb_field_iter_t *iter) { - pb_type_t type; - type = iter->pos->type; - - if (PB_HTYPE(type) == PB_HTYPE_ONEOF) { - if (*(pb_size_t *)iter->pSize != iter->pos->tag) - return; /* This is not the current field in the union */ - } - - /* Release anything contained inside an extension or submsg. - * This has to be done even if the submsg itself is statically - * allocated. */ - if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) { - /* Release fields from all extensions in the linked list */ - pb_extension_t *ext = *(pb_extension_t **)iter->pData; - while (ext != NULL) { - pb_field_iter_t ext_iter; - iter_from_extension(&ext_iter, ext); - pb_release_single_field(&ext_iter); - ext = ext->next; - } - } else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && - PB_ATYPE(type) != PB_ATYPE_CALLBACK) { - /* Release fields in submessage or submsg array */ - void *pItem = iter->pData; - pb_size_t count = 1; - - if (PB_ATYPE(type) == PB_ATYPE_POINTER) { - pItem = *(void **)iter->pData; - } - - if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { - if (PB_ATYPE(type) == PB_ATYPE_STATIC && iter->pSize == iter->pData) { - /* No _count field so use size of the array */ - count = iter->pos->array_size; - } else { - count = *(pb_size_t *)iter->pSize; - } - - if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > iter->pos->array_size) { - /* Protect against corrupted _count fields */ - count = iter->pos->array_size; - } - } - - if (pItem) { - while (count--) { - pb_release((const pb_field_t *)iter->pos->ptr, pItem); - pItem = (char *)pItem + iter->pos->data_size; - } - } - } - - if (PB_ATYPE(type) == PB_ATYPE_POINTER) { - if (PB_HTYPE(type) == PB_HTYPE_REPEATED && - (PB_LTYPE(type) == PB_LTYPE_STRING || - PB_LTYPE(type) == PB_LTYPE_BYTES)) { - /* Release entries in repeated string or bytes array */ - void **pItem = *(void ***)iter->pData; - pb_size_t count = *(pb_size_t *)iter->pSize; - while (count--) { - pb_free(*pItem); - *pItem++ = NULL; - } - } - - if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { - /* We are going to release the array, so set the size to 0 */ - *(pb_size_t *)iter->pSize = 0; - } - - /* Release main item */ - pb_free(*(void **)iter->pData); - *(void **)iter->pData = NULL; - } -} - -void pb_release(const pb_field_t fields[], void *dest_struct) { - pb_field_iter_t iter; - - if (!dest_struct) return; /* Ignore NULL pointers, similar to free() */ - - if (!pb_field_iter_begin(&iter, fields, dest_struct)) - return; /* Empty message type */ - - do { - pb_release_single_field(&iter); - } while (pb_field_iter_next(&iter)); -} -#endif - -/* Field decoders */ - -bool pb_decode_bool(pb_istream_t *stream, bool *dest) { - return pb_dec_bool(stream, NULL, (void *)dest); -} - -bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) { - pb_uint64_t value; - if (!pb_decode_varint(stream, &value)) return false; - - if (value & 1) - *dest = (pb_int64_t)(~(value >> 1)); - else - *dest = (pb_int64_t)(value >> 1); - - return true; -} - -bool pb_decode_fixed32(pb_istream_t *stream, void *dest) { - pb_byte_t bytes[4]; - - if (!pb_read(stream, bytes, 4)) return false; - - *(uint32_t *)dest = ((uint32_t)bytes[0] << 0) | ((uint32_t)bytes[1] << 8) | - ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24); - return true; -} - -#ifndef PB_WITHOUT_64BIT -bool pb_decode_fixed64(pb_istream_t *stream, void *dest) { - pb_byte_t bytes[8]; - - if (!pb_read(stream, bytes, 8)) return false; - - *(uint64_t *)dest = ((uint64_t)bytes[0] << 0) | ((uint64_t)bytes[1] << 8) | - ((uint64_t)bytes[2] << 16) | ((uint64_t)bytes[3] << 24) | - ((uint64_t)bytes[4] << 32) | ((uint64_t)bytes[5] << 40) | - ((uint64_t)bytes[6] << 48) | ((uint64_t)bytes[7] << 56); - - return true; -} -#endif - -static bool checkreturn pb_dec_bool(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - uint32_t value; - PB_UNUSED(field); - if (!pb_decode_varint32(stream, &value)) return false; - - *(bool *)dest = (value != 0); - return true; -} - -static bool checkreturn pb_dec_varint(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - pb_uint64_t value; - pb_int64_t svalue; - pb_int64_t clamped; - if (!pb_decode_varint(stream, &value)) return false; - - /* See issue 97: Google's C++ protobuf allows negative varint values to - * be cast as int32_t, instead of the int64_t that should be used when - * encoding. Previous nanopb versions had a bug in encoding. In order to - * not break decoding of such messages, we cast <=32 bit fields to - * int32_t first to get the sign correct. - */ - if (field->data_size == sizeof(pb_int64_t)) - svalue = (pb_int64_t)value; - else - svalue = (int32_t)value; - - /* Cast to the proper field size, while checking for overflows */ - if (field->data_size == sizeof(pb_int64_t)) - clamped = *(pb_int64_t *)dest = svalue; - else if (field->data_size == sizeof(int32_t)) - clamped = *(int32_t *)dest = (int32_t)svalue; - else if (field->data_size == sizeof(int_least16_t)) - clamped = *(int_least16_t *)dest = (int_least16_t)svalue; - else if (field->data_size == sizeof(int_least8_t)) - clamped = *(int_least8_t *)dest = (int_least8_t)svalue; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - if (clamped != svalue) PB_RETURN_ERROR(stream, "integer too large"); - - return true; -} - -static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - pb_uint64_t value, clamped; - if (!pb_decode_varint(stream, &value)) return false; - - /* Cast to the proper field size, while checking for overflows */ - if (field->data_size == sizeof(pb_uint64_t)) - clamped = *(pb_uint64_t *)dest = value; - else if (field->data_size == sizeof(uint32_t)) - clamped = *(uint32_t *)dest = (uint32_t)value; - else if (field->data_size == sizeof(uint_least16_t)) - clamped = *(uint_least16_t *)dest = (uint_least16_t)value; - else if (field->data_size == sizeof(uint_least8_t)) - clamped = *(uint_least8_t *)dest = (uint_least8_t)value; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - if (clamped != value) PB_RETURN_ERROR(stream, "integer too large"); - - return true; -} - -static bool checkreturn pb_dec_svarint(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - pb_int64_t value, clamped; - if (!pb_decode_svarint(stream, &value)) return false; - - /* Cast to the proper field size, while checking for overflows */ - if (field->data_size == sizeof(pb_int64_t)) - clamped = *(pb_int64_t *)dest = value; - else if (field->data_size == sizeof(int32_t)) - clamped = *(int32_t *)dest = (int32_t)value; - else if (field->data_size == sizeof(int_least16_t)) - clamped = *(int_least16_t *)dest = (int_least16_t)value; - else if (field->data_size == sizeof(int_least8_t)) - clamped = *(int_least8_t *)dest = (int_least8_t)value; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - if (clamped != value) PB_RETURN_ERROR(stream, "integer too large"); - - return true; -} - -static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - PB_UNUSED(field); - return pb_decode_fixed32(stream, dest); -} - -static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - PB_UNUSED(field); -#ifndef PB_WITHOUT_64BIT - return pb_decode_fixed64(stream, dest); -#else - PB_UNUSED(dest); - PB_RETURN_ERROR(stream, "no 64bit support"); -#endif -} - -static bool checkreturn pb_dec_bytes(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - uint32_t size; - size_t alloc_size; - pb_bytes_array_t *bdest; - - if (!pb_decode_varint32(stream, &size)) return false; - - if (size > PB_SIZE_MAX) PB_RETURN_ERROR(stream, "bytes overflow"); - - alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); - if (size > alloc_size) PB_RETURN_ERROR(stream, "size too large"); - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { -#ifndef PB_ENABLE_MALLOC - PB_RETURN_ERROR(stream, "no malloc support"); -#else - if (!allocate_field(stream, dest, alloc_size, 1)) return false; - bdest = *(pb_bytes_array_t **)dest; -#endif - } else { - if (alloc_size > field->data_size) - PB_RETURN_ERROR(stream, "bytes overflow"); - bdest = (pb_bytes_array_t *)dest; - } - - bdest->size = (pb_size_t)size; - return pb_read(stream, bdest->bytes, size); -} - -static bool checkreturn pb_dec_string(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - uint32_t size; - size_t alloc_size; - bool status; - if (!pb_decode_varint32(stream, &size)) return false; - - /* Space for null terminator */ - alloc_size = size + 1; - - if (alloc_size < size) PB_RETURN_ERROR(stream, "size too large"); - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { -#ifndef PB_ENABLE_MALLOC - PB_RETURN_ERROR(stream, "no malloc support"); -#else - if (!allocate_field(stream, dest, alloc_size, 1)) return false; - dest = *(void **)dest; -#endif - } else { - if (alloc_size > field->data_size) - PB_RETURN_ERROR(stream, "string overflow"); - } - - status = pb_read(stream, (pb_byte_t *)dest, size); - *((pb_byte_t *)dest + size) = 0; - return status; -} - -static bool checkreturn pb_dec_submessage(pb_istream_t *stream, - const pb_field_t *field, void *dest) { - bool status; - pb_istream_t substream; - const pb_field_t *submsg_fields = (const pb_field_t *)field->ptr; - - if (!pb_make_string_substream(stream, &substream)) return false; - - if (field->ptr == NULL) PB_RETURN_ERROR(stream, "invalid field descriptor"); - - /* New array entries need to be initialized, while required and optional - * submessages have already been initialized in the top-level pb_decode. */ - if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) - status = pb_decode(&substream, submsg_fields, dest); - else - status = pb_decode_noinit(&substream, submsg_fields, dest); - - if (!pb_close_string_substream(stream, &substream)) return false; - return status; -} - -static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, - const pb_field_t *field, - void *dest) { - uint32_t size; - - if (!pb_decode_varint32(stream, &size)) return false; - - if (size > PB_SIZE_MAX) PB_RETURN_ERROR(stream, "bytes overflow"); - - if (size == 0) { - /* As a special case, treat empty bytes string as all zeros for - * fixed_length_bytes. */ - memset(dest, 0, field->data_size); - return true; - } - - if (size != field->data_size) - PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); - - return pb_read(stream, (pb_byte_t *)dest, field->data_size); -} diff --git a/lib/transport/pb_encode.c b/lib/transport/pb_encode.c deleted file mode 100644 index 2409323e6..000000000 --- a/lib/transport/pb_encode.c +++ /dev/null @@ -1,838 +0,0 @@ -/* pb_encode.c -- encode a protobuf using minimal resources - * - * 2011 Petteri Aimonen - */ - -#include "pb.h" -#include "pb_encode.h" -#include "pb_common.h" - -/* Use the GCC warn_unused_result attribute to check that all return values - * are propagated correctly. On other compilers and gcc before 3.4.0 just - * ignore the annotation. - */ -#if !defined(__GNUC__) || (__GNUC__ < 3) || \ - (__GNUC__ == 3 && __GNUC_MINOR__ < 4) -#define checkreturn -#else -#define checkreturn __attribute__((warn_unused_result)) -#endif - -/************************************** - * Declarations internal to this file * - **************************************/ -typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, - const void *src) checkreturn; - -static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, - size_t count); -static bool checkreturn encode_array(pb_ostream_t *stream, - const pb_field_t *field, const void *pData, - size_t count, pb_encoder_t func); -static bool checkreturn encode_field(pb_ostream_t *stream, - const pb_field_t *field, - const void *pData); -static bool checkreturn default_extension_encoder( - pb_ostream_t *stream, const pb_extension_t *extension); -static bool checkreturn encode_extension_field(pb_ostream_t *stream, - const pb_field_t *field, - const void *pData); -static void *pb_const_cast(const void *p); -static bool checkreturn pb_enc_bool(pb_ostream_t *stream, - const pb_field_t *field, const void *src); -static bool checkreturn pb_enc_varint(pb_ostream_t *stream, - const pb_field_t *field, const void *src); -static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, - const pb_field_t *field, - const void *src); -static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, - const pb_field_t *field, - const void *src); -static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, - const pb_field_t *field, - const void *src); -static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, - const pb_field_t *field, - const void *src); -static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, - const pb_field_t *field, const void *src); -static bool checkreturn pb_enc_string(pb_ostream_t *stream, - const pb_field_t *field, const void *src); -static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, - const pb_field_t *field, - const void *src); -static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, - const pb_field_t *field, - const void *src); - -#ifdef PB_WITHOUT_64BIT -#define pb_int64_t int32_t -#define pb_uint64_t uint32_t - -static bool checkreturn pb_encode_negative_varint(pb_ostream_t *stream, - pb_uint64_t value); -#else -#define pb_int64_t int64_t -#define pb_uint64_t uint64_t -#endif - -/* --- Function pointers to field encoders --- - * Order in the array must match pb_action_t LTYPE numbering. - */ -static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = { - &pb_enc_bool, - &pb_enc_varint, - &pb_enc_uvarint, - &pb_enc_svarint, - &pb_enc_fixed32, - &pb_enc_fixed64, - - &pb_enc_bytes, - &pb_enc_string, - &pb_enc_submessage, - NULL, /* extensions */ - &pb_enc_fixed_length_bytes}; - -/******************************* - * pb_ostream_t implementation * - *******************************/ - -static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, - size_t count) { - size_t i; - pb_byte_t *dest = (pb_byte_t *)stream->state; - stream->state = dest + count; - - for (i = 0; i < count; i++) dest[i] = buf[i]; - - return true; -} - -pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) { - pb_ostream_t stream; -#ifdef PB_BUFFER_ONLY - stream.callback = (void *)1; /* Just a marker value */ -#else - stream.callback = &buf_write; -#endif - stream.state = buf; - stream.max_size = bufsize; - stream.bytes_written = 0; -#ifndef PB_NO_ERRMSG - stream.errmsg = NULL; -#endif - return stream; -} - -bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, - size_t count) { - if (count > 0 && stream->callback != NULL) { - if (stream->bytes_written + count > stream->max_size) - PB_RETURN_ERROR(stream, "stream full"); - -#ifdef PB_BUFFER_ONLY - if (!buf_write(stream, buf, count)) PB_RETURN_ERROR(stream, "io error"); -#else - if (!stream->callback(stream, buf, count)) - PB_RETURN_ERROR(stream, "io error"); -#endif - } - - stream->bytes_written += count; - return true; -} - -/************************* - * Encode a single field * - *************************/ - -/* Read a bool value without causing undefined behavior even if the value - * is invalid. See issue #434 and - * https://stackoverflow.com/questions/27661768/weird-results-for-conditional - */ -static bool safe_read_bool(const void *pSize) { - const char *p = (const char *)pSize; - size_t i; - for (i = 0; i < sizeof(bool); i++) { - if (p[i] != 0) return true; - } - return false; -} - -/* Encode a static array. Handles the size calculations and possible packing. */ -static bool checkreturn encode_array(pb_ostream_t *stream, - const pb_field_t *field, const void *pData, - size_t count, pb_encoder_t func) { - size_t i; - const void *p; -#ifndef PB_ENCODE_ARRAYS_UNPACKED - size_t size; -#endif - - if (count == 0) return true; - - if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) - PB_RETURN_ERROR(stream, "array max size exceeded"); - -#ifndef PB_ENCODE_ARRAYS_UNPACKED - /* We always pack arrays if the datatype allows it. */ - if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) { - if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) return false; - - /* Determine the total size of packed array. */ - if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) { - size = 4 * count; - } else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) { - size = 8 * count; - } else { - pb_ostream_t sizestream = PB_OSTREAM_SIZING; - p = pData; - for (i = 0; i < count; i++) { - if (!func(&sizestream, field, p)) return false; - p = (const char *)p + field->data_size; - } - size = sizestream.bytes_written; - } - - if (!pb_encode_varint(stream, (pb_uint64_t)size)) return false; - - if (stream->callback == NULL) - return pb_write(stream, NULL, size); /* Just sizing.. */ - - /* Write the data */ - p = pData; - for (i = 0; i < count; i++) { - if (!func(stream, field, p)) return false; - p = (const char *)p + field->data_size; - } - } else -#endif - { - p = pData; - for (i = 0; i < count; i++) { - if (!pb_encode_tag_for_field(stream, field)) return false; - - /* Normally the data is stored directly in the array entries, but - * for pointer-type string and bytes fields, the array entries are - * actually pointers themselves also. So we have to dereference once - * more to get to the actual data. */ - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && - (PB_LTYPE(field->type) == PB_LTYPE_STRING || - PB_LTYPE(field->type) == PB_LTYPE_BYTES)) { - if (!func(stream, field, *(const void *const *)p)) return false; - } else { - if (!func(stream, field, p)) return false; - } - p = (const char *)p + field->data_size; - } - } - - return true; -} - -/* In proto3, all fields are optional and are only encoded if their value is - * "non-zero". This function implements the check for the zero value. */ -static bool pb_check_proto3_default_value(const pb_field_t *field, - const void *pData) { - pb_type_t type = field->type; - const void *pSize = (const char *)pData + field->size_offset; - - if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) { - /* Required proto2 fields inside proto3 submessage, pretty rare case */ - return false; - } else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { - /* Repeated fields inside proto3 submessage: present if count != 0 */ - if (field->size_offset != 0) - return *(const pb_size_t *)pSize == 0; - else - return false; /* Fixed length array */ - } else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) { - /* Oneof fields */ - return *(const pb_size_t *)pSize == 0; - } else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->size_offset != 0) { - /* Proto2 optional fields inside proto3 submessage */ - return safe_read_bool(pSize) == false; - } - - /* Rest is proto3 singular fields */ - - if (PB_ATYPE(type) == PB_ATYPE_STATIC) { - if (PB_LTYPE(type) == PB_LTYPE_BYTES) { - const pb_bytes_array_t *bytes = (const pb_bytes_array_t *)pData; - return bytes->size == 0; - } else if (PB_LTYPE(type) == PB_LTYPE_STRING) { - return *(const char *)pData == '\0'; - } else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) { - /* Fixed length bytes is only empty if its length is fixed - * as 0. Which would be pretty strange, but we can check - * it anyway. */ - return field->data_size == 0; - } else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) { - /* Check all fields in the submessage to find if any of them - * are non-zero. The comparison cannot be done byte-per-byte - * because the C struct may contain padding bytes that must - * be skipped. - */ - pb_field_iter_t iter; - if (pb_field_iter_begin(&iter, (const pb_field_t *)field->ptr, - pb_const_cast(pData))) { - do { - if (!pb_check_proto3_default_value(iter.pos, iter.pData)) { - return false; - } - } while (pb_field_iter_next(&iter)); - } - return true; - } - } - - { - /* Catch-all branch that does byte-per-byte comparison for zero value. - * - * This is for all pointer fields, and for static PB_LTYPE_VARINT, - * UVARINT, SVARINT, FIXED32, FIXED64, EXTENSION fields, and also - * callback fields. These all have integer or pointer value which - * can be compared with 0. - */ - pb_size_t i; - const char *p = (const char *)pData; - for (i = 0; i < field->data_size; i++) { - if (p[i] != 0) { - return false; - } - } - - return true; - } -} - -/* Encode a field with static or pointer allocation, i.e. one whose data - * is available to the encoder directly. */ -static bool checkreturn encode_basic_field(pb_ostream_t *stream, - const pb_field_t *field, - const void *pData) { - pb_encoder_t func; - bool implicit_has; - const void *pSize = &implicit_has; - - func = PB_ENCODERS[PB_LTYPE(field->type)]; - - if (field->size_offset) { - /* Static optional, repeated or oneof field */ - pSize = (const char *)pData + field->size_offset; - } else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) { - /* Proto3 style field, optional but without explicit has_ field. */ - implicit_has = !pb_check_proto3_default_value(field, pData); - } else { - /* Required field, always present */ - implicit_has = true; - } - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { - /* pData is a pointer to the field, which contains pointer to - * the data. If the 2nd pointer is NULL, it is interpreted as if - * the has_field was false. - */ - pData = *(const void *const *)pData; - implicit_has = (pData != NULL); - } - - switch (PB_HTYPE(field->type)) { - case PB_HTYPE_REQUIRED: - if (!pData) PB_RETURN_ERROR(stream, "missing required field"); - if (!pb_encode_tag_for_field(stream, field)) return false; - if (!func(stream, field, pData)) return false; - break; - - case PB_HTYPE_OPTIONAL: - if (safe_read_bool(pSize)) { - if (!pb_encode_tag_for_field(stream, field)) return false; - - if (!func(stream, field, pData)) return false; - } - break; - - case PB_HTYPE_REPEATED: { - pb_size_t count; - if (field->size_offset != 0) { - count = *(const pb_size_t *)pSize; - } else { - count = field->array_size; - } - if (!encode_array(stream, field, pData, count, func)) return false; - break; - } - - case PB_HTYPE_ONEOF: - if (*(const pb_size_t *)pSize == field->tag) { - if (!pb_encode_tag_for_field(stream, field)) return false; - - if (!func(stream, field, pData)) return false; - } - break; - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } - - return true; -} - -/* Encode a field with callback semantics. This means that a user function is - * called to provide and encode the actual data. */ -static bool checkreturn encode_callback_field(pb_ostream_t *stream, - const pb_field_t *field, - const void *pData) { - const pb_callback_t *callback = (const pb_callback_t *)pData; - -#ifdef PB_OLD_CALLBACK_STYLE - const void *arg = callback->arg; -#else - void *const *arg = &(callback->arg); -#endif - - if (callback->funcs.encode != NULL) { - if (!callback->funcs.encode(stream, field, arg)) - PB_RETURN_ERROR(stream, "callback error"); - } - return true; -} - -/* Encode a single field of any callback or static type. */ -static bool checkreturn encode_field(pb_ostream_t *stream, - const pb_field_t *field, - const void *pData) { - switch (PB_ATYPE(field->type)) { - case PB_ATYPE_STATIC: - case PB_ATYPE_POINTER: - return encode_basic_field(stream, field, pData); - - case PB_ATYPE_CALLBACK: - return encode_callback_field(stream, field, pData); - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -} - -/* Default handler for extension fields. Expects to have a pb_field_t - * pointer in the extension->type->arg field. */ -static bool checkreturn default_extension_encoder( - pb_ostream_t *stream, const pb_extension_t *extension) { - const pb_field_t *field = (const pb_field_t *)extension->type->arg; - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { - /* For pointer extensions, the pointer is stored directly - * in the extension structure. This avoids having an extra - * indirection. */ - return encode_field(stream, field, &extension->dest); - } else { - return encode_field(stream, field, extension->dest); - } -} - -/* Walk through all the registered extensions and give them a chance - * to encode themselves. */ -static bool checkreturn encode_extension_field(pb_ostream_t *stream, - const pb_field_t *field, - const void *pData) { - const pb_extension_t *extension = *(const pb_extension_t *const *)pData; - PB_UNUSED(field); - - while (extension) { - bool status; - if (extension->type->encode) - status = extension->type->encode(stream, extension); - else - status = default_extension_encoder(stream, extension); - - if (!status) return false; - - extension = extension->next; - } - - return true; -} - -/********************* - * Encode all fields * - *********************/ - -static void *pb_const_cast(const void *p) { - /* Note: this casts away const, in order to use the common field iterator - * logic for both encoding and decoding. */ - union { - void *p1; - const void *p2; - } t; - t.p2 = p; - return t.p1; -} - -bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], - const void *src_struct) { - pb_field_iter_t iter; - if (!pb_field_iter_begin(&iter, fields, pb_const_cast(src_struct))) - return true; /* Empty message type */ - - do { - if (PB_LTYPE(iter.pos->type) == PB_LTYPE_EXTENSION) { - /* Special case for the extension field placeholder */ - if (!encode_extension_field(stream, iter.pos, iter.pData)) return false; - } else { - /* Regular field */ - if (!encode_field(stream, iter.pos, iter.pData)) return false; - } - } while (pb_field_iter_next(&iter)); - - return true; -} - -bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], - const void *src_struct) { - return pb_encode_submessage(stream, fields, src_struct); -} - -bool pb_encode_nullterminated(pb_ostream_t *stream, const pb_field_t fields[], - const void *src_struct) { - const pb_byte_t zero = 0; - - if (!pb_encode(stream, fields, src_struct)) return false; - - return pb_write(stream, &zero, 1); -} - -bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], - const void *src_struct) { - pb_ostream_t stream = PB_OSTREAM_SIZING; - - if (!pb_encode(&stream, fields, src_struct)) return false; - - *size = stream.bytes_written; - return true; -} - -/******************** - * Helper functions * - ********************/ - -#ifdef PB_WITHOUT_64BIT -bool checkreturn pb_encode_negative_varint(pb_ostream_t *stream, - pb_uint64_t value) { - pb_byte_t buffer[10]; - size_t i = 0; - size_t compensation = 32; /* we need to compensate 32 bits all set to 1 */ - - while (value) { - buffer[i] = (pb_byte_t)((value & 0x7F) | 0x80); - value >>= 7; - if (compensation) { - /* re-set all the compensation bits we can or need */ - size_t bits = compensation > 7 ? 7 : compensation; - value ^= (pb_uint64_t)((0xFFu >> (8 - bits)) - << 25); /* set the number of bits needed on the - lowest of the most significant 7 bits */ - compensation -= bits; - } - i++; - } - buffer[i - 1] &= 0x7F; /* Unset top bit on last byte */ - - return pb_write(stream, buffer, i); -} -#endif - -bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) { - pb_byte_t buffer[10]; - size_t i = 0; - - if (value <= 0x7F) { - pb_byte_t v = (pb_byte_t)value; - return pb_write(stream, &v, 1); - } - - while (value) { - buffer[i] = (pb_byte_t)((value & 0x7F) | 0x80); - value >>= 7; - i++; - } - buffer[i - 1] &= 0x7F; /* Unset top bit on last byte */ - - return pb_write(stream, buffer, i); -} - -bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) { - pb_uint64_t zigzagged; - if (value < 0) - zigzagged = ~((pb_uint64_t)value << 1); - else - zigzagged = (pb_uint64_t)value << 1; - - return pb_encode_varint(stream, zigzagged); -} - -bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) { - uint32_t val = *(const uint32_t *)value; - pb_byte_t bytes[4]; - bytes[0] = (pb_byte_t)(val & 0xFF); - bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); - bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); - bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); - return pb_write(stream, bytes, 4); -} - -#ifndef PB_WITHOUT_64BIT -bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) { - uint64_t val = *(const uint64_t *)value; - pb_byte_t bytes[8]; - bytes[0] = (pb_byte_t)(val & 0xFF); - bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); - bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); - bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); - bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); - bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); - bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); - bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); - return pb_write(stream, bytes, 8); -} -#endif - -bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, - uint32_t field_number) { - pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; - return pb_encode_varint(stream, tag); -} - -bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, - const pb_field_t *field) { - pb_wire_type_t wiretype; - switch (PB_LTYPE(field->type)) { - case PB_LTYPE_BOOL: - case PB_LTYPE_VARINT: - case PB_LTYPE_UVARINT: - case PB_LTYPE_SVARINT: - wiretype = PB_WT_VARINT; - break; - - case PB_LTYPE_FIXED32: - wiretype = PB_WT_32BIT; - break; - - case PB_LTYPE_FIXED64: - wiretype = PB_WT_64BIT; - break; - - case PB_LTYPE_BYTES: - case PB_LTYPE_STRING: - case PB_LTYPE_SUBMESSAGE: - case PB_LTYPE_FIXED_LENGTH_BYTES: - wiretype = PB_WT_STRING; - break; - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } - - return pb_encode_tag(stream, wiretype, field->tag); -} - -bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, - size_t size) { - if (!pb_encode_varint(stream, (pb_uint64_t)size)) return false; - - return pb_write(stream, buffer, size); -} - -bool checkreturn pb_encode_submessage(pb_ostream_t *stream, - const pb_field_t fields[], - const void *src_struct) { - /* First calculate the message size using a non-writing substream. */ - pb_ostream_t substream = PB_OSTREAM_SIZING; - size_t size; - bool status; - - if (!pb_encode(&substream, fields, src_struct)) { -#ifndef PB_NO_ERRMSG - stream->errmsg = substream.errmsg; -#endif - return false; - } - - size = substream.bytes_written; - - if (!pb_encode_varint(stream, (pb_uint64_t)size)) return false; - - if (stream->callback == NULL) - return pb_write(stream, NULL, size); /* Just sizing */ - - if (stream->bytes_written + size > stream->max_size) - PB_RETURN_ERROR(stream, "stream full"); - - /* Use a substream to verify that a callback doesn't write more than - * what it did the first time. */ - substream.callback = stream->callback; - substream.state = stream->state; - substream.max_size = size; - substream.bytes_written = 0; -#ifndef PB_NO_ERRMSG - substream.errmsg = NULL; -#endif - - status = pb_encode(&substream, fields, src_struct); - - stream->bytes_written += substream.bytes_written; - stream->state = substream.state; -#ifndef PB_NO_ERRMSG - stream->errmsg = substream.errmsg; -#endif - - if (substream.bytes_written != size) - PB_RETURN_ERROR(stream, "submsg size changed"); - - return status; -} - -/* Field encoders */ - -static bool checkreturn pb_enc_bool(pb_ostream_t *stream, - const pb_field_t *field, const void *src) { - uint32_t value = (uint32_t)safe_read_bool(src); - PB_UNUSED(field); - return pb_encode_varint(stream, value); -} - -static bool checkreturn pb_enc_varint(pb_ostream_t *stream, - const pb_field_t *field, - const void *src) { - pb_int64_t value = 0; - - if (field->data_size == sizeof(int_least8_t)) - value = *(const int_least8_t *)src; - else if (field->data_size == sizeof(int_least16_t)) - value = *(const int_least16_t *)src; - else if (field->data_size == sizeof(int32_t)) - value = *(const int32_t *)src; - else if (field->data_size == sizeof(pb_int64_t)) - value = *(const pb_int64_t *)src; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - -#ifdef PB_WITHOUT_64BIT - if (value < 0) - return pb_encode_negative_varint(stream, (pb_uint64_t)value); - else -#endif - return pb_encode_varint(stream, (pb_uint64_t)value); -} - -static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, - const pb_field_t *field, - const void *src) { - pb_uint64_t value = 0; - - if (field->data_size == sizeof(uint_least8_t)) - value = *(const uint_least8_t *)src; - else if (field->data_size == sizeof(uint_least16_t)) - value = *(const uint_least16_t *)src; - else if (field->data_size == sizeof(uint32_t)) - value = *(const uint32_t *)src; - else if (field->data_size == sizeof(pb_uint64_t)) - value = *(const pb_uint64_t *)src; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - return pb_encode_varint(stream, value); -} - -static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, - const pb_field_t *field, - const void *src) { - pb_int64_t value = 0; - - if (field->data_size == sizeof(int_least8_t)) - value = *(const int_least8_t *)src; - else if (field->data_size == sizeof(int_least16_t)) - value = *(const int_least16_t *)src; - else if (field->data_size == sizeof(int32_t)) - value = *(const int32_t *)src; - else if (field->data_size == sizeof(pb_int64_t)) - value = *(const pb_int64_t *)src; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - return pb_encode_svarint(stream, value); -} - -static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, - const pb_field_t *field, - const void *src) { - PB_UNUSED(field); -#ifndef PB_WITHOUT_64BIT - return pb_encode_fixed64(stream, src); -#else - PB_UNUSED(src); - PB_RETURN_ERROR(stream, "no 64bit support"); -#endif -} - -static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, - const pb_field_t *field, - const void *src) { - PB_UNUSED(field); - return pb_encode_fixed32(stream, src); -} - -static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, - const pb_field_t *field, const void *src) { - const pb_bytes_array_t *bytes = NULL; - - bytes = (const pb_bytes_array_t *)src; - - if (src == NULL) { - /* Treat null pointer as an empty bytes field */ - return pb_encode_string(stream, NULL, 0); - } - - if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && - PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size) { - PB_RETURN_ERROR(stream, "bytes size exceeded"); - } - - return pb_encode_string(stream, bytes->bytes, bytes->size); -} - -static bool checkreturn pb_enc_string(pb_ostream_t *stream, - const pb_field_t *field, - const void *src) { - size_t size = 0; - size_t max_size = field->data_size; - const char *p = (const char *)src; - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) max_size = (size_t)-1; - - if (src == NULL) { - size = 0; /* Treat null pointer as an empty string */ - } else { - /* strnlen() is not always available, so just use a loop */ - while (size < max_size && *p != '\0') { - size++; - p++; - } - } - - return pb_encode_string(stream, (const pb_byte_t *)src, size); -} - -static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, - const pb_field_t *field, - const void *src) { - if (field->ptr == NULL) PB_RETURN_ERROR(stream, "invalid field descriptor"); - - return pb_encode_submessage(stream, (const pb_field_t *)field->ptr, src); -} - -static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, - const pb_field_t *field, - const void *src) { - return pb_encode_string(stream, (const pb_byte_t *)src, field->data_size); -} diff --git a/lib/variant/CMakeLists.txt b/lib/variant/CMakeLists.txt index 0728f3f28..99ff2119c 100644 --- a/lib/variant/CMakeLists.txt +++ b/lib/variant/CMakeLists.txt @@ -3,9 +3,15 @@ include_directories( ${CMAKE_BINARY_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}) -add_library(kkvariant.keepkey - ${CMAKE_CURRENT_SOURCE_DIR}/keepkey/keepkey.c - ${CMAKE_CURRENT_SOURCE_DIR}/keepkey/logo.c) +if("${COIN_SUPPORT}" STREQUAL "BTC") + add_library(kkvariant.keepkey + ${CMAKE_CURRENT_SOURCE_DIR}/keepkey/keepkey.c + ${CMAKE_CURRENT_SOURCE_DIR}/keepkey/logobtc.c) +else() + add_library(kkvariant.keepkey + ${CMAKE_CURRENT_SOURCE_DIR}/keepkey/keepkey.c + ${CMAKE_CURRENT_SOURCE_DIR}/keepkey/logo.c) +endif() add_library(kkvariant.salt ${CMAKE_CURRENT_SOURCE_DIR}/salt/salt.c diff --git a/lib/variant/keepkey/logo.c b/lib/variant/keepkey/logo.c index e3fe0115c..0a05fc7af 100644 --- a/lib/variant/keepkey/logo.c +++ b/lib/variant/keepkey/logo.c @@ -19,6 +19,15 @@ #include "keepkey/board/resources.h" +#ifdef DEV_DEBUG +// the spi display and clock diffs require a shorter animation frame time on the dev board +#define ATIME 21 // this is the screensaver move time +#define ATIME2 3 // this is the fade-in frame time +#else +#define ATIME 150 +#define ATIME2 25 +#endif + static const uint8_t kk_logo_1_data[1189] = { 0xfc, 0xb5, 0xf7, 0xeb, 0x58, 0x34, 0x00, 0x03, 0xff, 0xff, 0xdd, 0x34, 0x00, 0xff, 0xe2, 0x03, 0xff, 0x35, 0x00, 0x03, 0xff, 0x35, 0x00, 0x03, @@ -124,232 +133,232 @@ static const Image kk_logo_1_image = {56, 49, sizeof(kk_logo_1_data), kk_logo_1_data}; const VariantAnimation kk_logo = { - 21, {{100, 9, 25, 0, &kk_logo_1_image}, {100, 9, 25, 5, &kk_logo_1_image}, - {100, 9, 25, 10, &kk_logo_1_image}, {100, 9, 25, 15, &kk_logo_1_image}, - {100, 9, 25, 20, &kk_logo_1_image}, {100, 9, 25, 25, &kk_logo_1_image}, - {100, 9, 25, 30, &kk_logo_1_image}, {100, 9, 25, 35, &kk_logo_1_image}, - {100, 9, 25, 40, &kk_logo_1_image}, {100, 9, 25, 45, &kk_logo_1_image}, - {100, 9, 25, 50, &kk_logo_1_image}, {100, 9, 25, 55, &kk_logo_1_image}, - {100, 9, 25, 60, &kk_logo_1_image}, {100, 9, 25, 65, &kk_logo_1_image}, - {100, 9, 25, 70, &kk_logo_1_image}, {100, 9, 25, 75, &kk_logo_1_image}, - {100, 9, 25, 80, &kk_logo_1_image}, {100, 9, 25, 85, &kk_logo_1_image}, - {100, 9, 25, 90, &kk_logo_1_image}, {100, 9, 25, 95, &kk_logo_1_image}, - {100, 9, 25, 100, &kk_logo_1_image}}}; + 21, {{100, 9, ATIME2, 0, &kk_logo_1_image}, {100, 9, ATIME2, 5, &kk_logo_1_image}, + {100, 9, ATIME2, 10, &kk_logo_1_image}, {100, 9, ATIME2, 15, &kk_logo_1_image}, + {100, 9, ATIME2, 20, &kk_logo_1_image}, {100, 9, ATIME2, 25, &kk_logo_1_image}, + {100, 9, ATIME2, 30, &kk_logo_1_image}, {100, 9, ATIME2, 35, &kk_logo_1_image}, + {100, 9, ATIME2, 40, &kk_logo_1_image}, {100, 9, ATIME2, 45, &kk_logo_1_image}, + {100, 9, ATIME2, 50, &kk_logo_1_image}, {100, 9, ATIME2, 55, &kk_logo_1_image}, + {100, 9, ATIME2, 60, &kk_logo_1_image}, {100, 9, ATIME2, 65, &kk_logo_1_image}, + {100, 9, ATIME2, 70, &kk_logo_1_image}, {100, 9, ATIME2, 75, &kk_logo_1_image}, + {100, 9, ATIME2, 80, &kk_logo_1_image}, {100, 9, ATIME2, 85, &kk_logo_1_image}, + {100, 9, ATIME2, 90, &kk_logo_1_image}, {100, 9, ATIME2, 95, &kk_logo_1_image}, + {100, 9, ATIME2, 100, &kk_logo_1_image}}}; const VariantAnimation kk_logo_reversed = { 21, - {{100, 9, 25, 100, &kk_logo_1_image}, {100, 9, 25, 95, &kk_logo_1_image}, - {100, 9, 25, 90, &kk_logo_1_image}, {100, 9, 25, 85, &kk_logo_1_image}, - {100, 9, 25, 80, &kk_logo_1_image}, {100, 9, 25, 75, &kk_logo_1_image}, - {100, 9, 25, 70, &kk_logo_1_image}, {100, 9, 25, 65, &kk_logo_1_image}, - {100, 9, 25, 60, &kk_logo_1_image}, {100, 9, 25, 55, &kk_logo_1_image}, - {100, 9, 25, 50, &kk_logo_1_image}, {100, 9, 25, 45, &kk_logo_1_image}, - {100, 9, 25, 40, &kk_logo_1_image}, {100, 9, 25, 35, &kk_logo_1_image}, - {100, 9, 25, 30, &kk_logo_1_image}, {100, 9, 25, 25, &kk_logo_1_image}, - {100, 9, 25, 20, &kk_logo_1_image}, {100, 9, 25, 15, &kk_logo_1_image}, - {100, 9, 25, 10, &kk_logo_1_image}, {100, 9, 25, 5, &kk_logo_1_image}, - {100, 9, 25, 0, &kk_logo_1_image}}}; + {{100, 9, ATIME2, 100, &kk_logo_1_image}, {100, 9, ATIME2, 95, &kk_logo_1_image}, + {100, 9, ATIME2, 90, &kk_logo_1_image}, {100, 9, ATIME2, 85, &kk_logo_1_image}, + {100, 9, ATIME2, 80, &kk_logo_1_image}, {100, 9, ATIME2, 75, &kk_logo_1_image}, + {100, 9, ATIME2, 70, &kk_logo_1_image}, {100, 9, ATIME2, 65, &kk_logo_1_image}, + {100, 9, ATIME2, 60, &kk_logo_1_image}, {100, 9, ATIME2, 55, &kk_logo_1_image}, + {100, 9, ATIME2, 50, &kk_logo_1_image}, {100, 9, ATIME2, 45, &kk_logo_1_image}, + {100, 9, ATIME2, 40, &kk_logo_1_image}, {100, 9, ATIME2, 35, &kk_logo_1_image}, + {100, 9, ATIME2, 30, &kk_logo_1_image}, {100, 9, ATIME2, 25, &kk_logo_1_image}, + {100, 9, ATIME2, 20, &kk_logo_1_image}, {100, 9, ATIME2, 15, &kk_logo_1_image}, + {100, 9, ATIME2, 10, &kk_logo_1_image}, {100, 9, ATIME2, 5, &kk_logo_1_image}, + {100, 9, ATIME2, 0, &kk_logo_1_image}}}; const VariantAnimation kk_screensaver = { 199, { - {100, 9, 150, 60, &kk_logo_1_image}, - {102, 9, 150, 60, &kk_logo_1_image}, - {104, 9, 150, 60, &kk_logo_1_image}, - {106, 9, 150, 60, &kk_logo_1_image}, - {108, 9, 150, 60, &kk_logo_1_image}, - {110, 9, 150, 60, &kk_logo_1_image}, - {112, 9, 150, 60, &kk_logo_1_image}, - {114, 9, 150, 60, &kk_logo_1_image}, - {116, 9, 150, 60, &kk_logo_1_image}, - {118, 9, 150, 60, &kk_logo_1_image}, - {120, 9, 150, 60, &kk_logo_1_image}, - {122, 9, 150, 60, &kk_logo_1_image}, - {124, 9, 150, 60, &kk_logo_1_image}, - {126, 9, 150, 60, &kk_logo_1_image}, - {128, 9, 150, 60, &kk_logo_1_image}, - {130, 9, 150, 60, &kk_logo_1_image}, - {132, 9, 150, 60, &kk_logo_1_image}, - {134, 9, 150, 60, &kk_logo_1_image}, - {136, 9, 150, 60, &kk_logo_1_image}, - {138, 9, 150, 60, &kk_logo_1_image}, - {140, 9, 150, 60, &kk_logo_1_image}, - {142, 9, 150, 60, &kk_logo_1_image}, - {144, 9, 150, 60, &kk_logo_1_image}, - {146, 9, 150, 60, &kk_logo_1_image}, - {148, 9, 150, 60, &kk_logo_1_image}, - {150, 9, 150, 60, &kk_logo_1_image}, - {152, 9, 150, 60, &kk_logo_1_image}, - {154, 9, 150, 60, &kk_logo_1_image}, - {156, 9, 150, 60, &kk_logo_1_image}, - {158, 9, 150, 60, &kk_logo_1_image}, - {160, 9, 150, 60, &kk_logo_1_image}, - {162, 9, 150, 60, &kk_logo_1_image}, - {164, 9, 150, 60, &kk_logo_1_image}, - {166, 9, 150, 60, &kk_logo_1_image}, - {168, 9, 150, 60, &kk_logo_1_image}, - {170, 9, 150, 60, &kk_logo_1_image}, - {172, 9, 150, 60, &kk_logo_1_image}, - {174, 9, 150, 60, &kk_logo_1_image}, - {176, 9, 150, 60, &kk_logo_1_image}, - {178, 9, 150, 60, &kk_logo_1_image}, - {180, 9, 150, 60, &kk_logo_1_image}, - {182, 9, 150, 60, &kk_logo_1_image}, - {184, 9, 150, 60, &kk_logo_1_image}, - {186, 9, 150, 60, &kk_logo_1_image}, - {188, 9, 150, 60, &kk_logo_1_image}, - {190, 9, 150, 60, &kk_logo_1_image}, - {192, 9, 150, 60, &kk_logo_1_image}, - {194, 9, 150, 60, &kk_logo_1_image}, - {196, 9, 150, 60, &kk_logo_1_image}, - {198, 9, 150, 60, &kk_logo_1_image}, - {198, 9, 150, 60, &kk_logo_1_image}, - {196, 9, 150, 60, &kk_logo_1_image}, - {194, 9, 150, 60, &kk_logo_1_image}, - {192, 9, 150, 60, &kk_logo_1_image}, - {190, 9, 150, 60, &kk_logo_1_image}, - {188, 9, 150, 60, &kk_logo_1_image}, - {186, 9, 150, 60, &kk_logo_1_image}, - {184, 9, 150, 60, &kk_logo_1_image}, - {182, 9, 150, 60, &kk_logo_1_image}, - {180, 9, 150, 60, &kk_logo_1_image}, - {178, 9, 150, 60, &kk_logo_1_image}, - {176, 9, 150, 60, &kk_logo_1_image}, - {174, 9, 150, 60, &kk_logo_1_image}, - {172, 9, 150, 60, &kk_logo_1_image}, - {170, 9, 150, 60, &kk_logo_1_image}, - {168, 9, 150, 60, &kk_logo_1_image}, - {166, 9, 150, 60, &kk_logo_1_image}, - {164, 9, 150, 60, &kk_logo_1_image}, - {162, 9, 150, 60, &kk_logo_1_image}, - {160, 9, 150, 60, &kk_logo_1_image}, - {158, 9, 150, 60, &kk_logo_1_image}, - {156, 9, 150, 60, &kk_logo_1_image}, - {154, 9, 150, 60, &kk_logo_1_image}, - {152, 9, 150, 60, &kk_logo_1_image}, - {150, 9, 150, 60, &kk_logo_1_image}, - {148, 9, 150, 60, &kk_logo_1_image}, - {146, 9, 150, 60, &kk_logo_1_image}, - {144, 9, 150, 60, &kk_logo_1_image}, - {142, 9, 150, 60, &kk_logo_1_image}, - {140, 9, 150, 60, &kk_logo_1_image}, - {138, 9, 150, 60, &kk_logo_1_image}, - {136, 9, 150, 60, &kk_logo_1_image}, - {134, 9, 150, 60, &kk_logo_1_image}, - {132, 9, 150, 60, &kk_logo_1_image}, - {130, 9, 150, 60, &kk_logo_1_image}, - {128, 9, 150, 60, &kk_logo_1_image}, - {126, 9, 150, 60, &kk_logo_1_image}, - {124, 9, 150, 60, &kk_logo_1_image}, - {122, 9, 150, 60, &kk_logo_1_image}, - {120, 9, 150, 60, &kk_logo_1_image}, - {118, 9, 150, 60, &kk_logo_1_image}, - {116, 9, 150, 60, &kk_logo_1_image}, - {114, 9, 150, 60, &kk_logo_1_image}, - {112, 9, 150, 60, &kk_logo_1_image}, - {110, 9, 150, 60, &kk_logo_1_image}, - {108, 9, 150, 60, &kk_logo_1_image}, - {106, 9, 150, 60, &kk_logo_1_image}, - {104, 9, 150, 60, &kk_logo_1_image}, - {102, 9, 150, 60, &kk_logo_1_image}, - {100, 9, 150, 60, &kk_logo_1_image}, - {98, 9, 150, 60, &kk_logo_1_image}, - {96, 9, 150, 60, &kk_logo_1_image}, - {94, 9, 150, 60, &kk_logo_1_image}, - {92, 9, 150, 60, &kk_logo_1_image}, - {90, 9, 150, 60, &kk_logo_1_image}, - {88, 9, 150, 60, &kk_logo_1_image}, - {86, 9, 150, 60, &kk_logo_1_image}, - {84, 9, 150, 60, &kk_logo_1_image}, - {82, 9, 150, 60, &kk_logo_1_image}, - {80, 9, 150, 60, &kk_logo_1_image}, - {78, 9, 150, 60, &kk_logo_1_image}, - {76, 9, 150, 60, &kk_logo_1_image}, - {74, 9, 150, 60, &kk_logo_1_image}, - {72, 9, 150, 60, &kk_logo_1_image}, - {70, 9, 150, 60, &kk_logo_1_image}, - {68, 9, 150, 60, &kk_logo_1_image}, - {66, 9, 150, 60, &kk_logo_1_image}, - {64, 9, 150, 60, &kk_logo_1_image}, - {62, 9, 150, 60, &kk_logo_1_image}, - {60, 9, 150, 60, &kk_logo_1_image}, - {58, 9, 150, 60, &kk_logo_1_image}, - {56, 9, 150, 60, &kk_logo_1_image}, - {54, 9, 150, 60, &kk_logo_1_image}, - {52, 9, 150, 60, &kk_logo_1_image}, - {50, 9, 150, 60, &kk_logo_1_image}, - {48, 9, 150, 60, &kk_logo_1_image}, - {46, 9, 150, 60, &kk_logo_1_image}, - {44, 9, 150, 60, &kk_logo_1_image}, - {42, 9, 150, 60, &kk_logo_1_image}, - {40, 9, 150, 60, &kk_logo_1_image}, - {38, 9, 150, 60, &kk_logo_1_image}, - {36, 9, 150, 60, &kk_logo_1_image}, - {34, 9, 150, 60, &kk_logo_1_image}, - {32, 9, 150, 60, &kk_logo_1_image}, - {30, 9, 150, 60, &kk_logo_1_image}, - {28, 9, 150, 60, &kk_logo_1_image}, - {26, 9, 150, 60, &kk_logo_1_image}, - {24, 9, 150, 60, &kk_logo_1_image}, - {22, 9, 150, 60, &kk_logo_1_image}, - {20, 9, 150, 60, &kk_logo_1_image}, - {18, 9, 150, 60, &kk_logo_1_image}, - {16, 9, 150, 60, &kk_logo_1_image}, - {14, 9, 150, 60, &kk_logo_1_image}, - {12, 9, 150, 60, &kk_logo_1_image}, - {10, 9, 150, 60, &kk_logo_1_image}, - {8, 9, 150, 60, &kk_logo_1_image}, - {6, 9, 150, 60, &kk_logo_1_image}, - {4, 9, 150, 60, &kk_logo_1_image}, - {2, 9, 150, 60, &kk_logo_1_image}, - {0, 9, 150, 60, &kk_logo_1_image}, - {0, 9, 150, 60, &kk_logo_1_image}, - {2, 9, 150, 60, &kk_logo_1_image}, - {4, 9, 150, 60, &kk_logo_1_image}, - {6, 9, 150, 60, &kk_logo_1_image}, - {8, 9, 150, 60, &kk_logo_1_image}, - {10, 9, 150, 60, &kk_logo_1_image}, - {12, 9, 150, 60, &kk_logo_1_image}, - {14, 9, 150, 60, &kk_logo_1_image}, - {16, 9, 150, 60, &kk_logo_1_image}, - {18, 9, 150, 60, &kk_logo_1_image}, - {20, 9, 150, 60, &kk_logo_1_image}, - {22, 9, 150, 60, &kk_logo_1_image}, - {24, 9, 150, 60, &kk_logo_1_image}, - {26, 9, 150, 60, &kk_logo_1_image}, - {28, 9, 150, 60, &kk_logo_1_image}, - {30, 9, 150, 60, &kk_logo_1_image}, - {32, 9, 150, 60, &kk_logo_1_image}, - {34, 9, 150, 60, &kk_logo_1_image}, - {36, 9, 150, 60, &kk_logo_1_image}, - {38, 9, 150, 60, &kk_logo_1_image}, - {40, 9, 150, 60, &kk_logo_1_image}, - {42, 9, 150, 60, &kk_logo_1_image}, - {44, 9, 150, 60, &kk_logo_1_image}, - {46, 9, 150, 60, &kk_logo_1_image}, - {48, 9, 150, 60, &kk_logo_1_image}, - {50, 9, 150, 60, &kk_logo_1_image}, - {52, 9, 150, 60, &kk_logo_1_image}, - {54, 9, 150, 60, &kk_logo_1_image}, - {56, 9, 150, 60, &kk_logo_1_image}, - {58, 9, 150, 60, &kk_logo_1_image}, - {60, 9, 150, 60, &kk_logo_1_image}, - {62, 9, 150, 60, &kk_logo_1_image}, - {64, 9, 150, 60, &kk_logo_1_image}, - {66, 9, 150, 60, &kk_logo_1_image}, - {68, 9, 150, 60, &kk_logo_1_image}, - {70, 9, 150, 60, &kk_logo_1_image}, - {72, 9, 150, 60, &kk_logo_1_image}, - {74, 9, 150, 60, &kk_logo_1_image}, - {76, 9, 150, 60, &kk_logo_1_image}, - {78, 9, 150, 60, &kk_logo_1_image}, - {80, 9, 150, 60, &kk_logo_1_image}, - {82, 9, 150, 60, &kk_logo_1_image}, - {84, 9, 150, 60, &kk_logo_1_image}, - {86, 9, 150, 60, &kk_logo_1_image}, - {88, 9, 150, 60, &kk_logo_1_image}, - {90, 9, 150, 60, &kk_logo_1_image}, - {92, 9, 150, 60, &kk_logo_1_image}, - {94, 9, 150, 60, &kk_logo_1_image}, - {96, 9, 150, 60, &kk_logo_1_image}, - {98, 9, 150, 60, &kk_logo_1_image}, + {100, 9, ATIME, 160, &kk_logo_1_image}, + {102, 9, ATIME, 160, &kk_logo_1_image}, + {104, 9, ATIME, 160, &kk_logo_1_image}, + {106, 9, ATIME, 160, &kk_logo_1_image}, + {108, 9, ATIME, 160, &kk_logo_1_image}, + {110, 9, ATIME, 160, &kk_logo_1_image}, + {112, 9, ATIME, 160, &kk_logo_1_image}, + {114, 9, ATIME, 160, &kk_logo_1_image}, + {116, 9, ATIME, 160, &kk_logo_1_image}, + {118, 9, ATIME, 160, &kk_logo_1_image}, + {120, 9, ATIME, 160, &kk_logo_1_image}, + {122, 9, ATIME, 160, &kk_logo_1_image}, + {124, 9, ATIME, 160, &kk_logo_1_image}, + {126, 9, ATIME, 160, &kk_logo_1_image}, + {128, 9, ATIME, 160, &kk_logo_1_image}, + {130, 9, ATIME, 160, &kk_logo_1_image}, + {132, 9, ATIME, 160, &kk_logo_1_image}, + {134, 9, ATIME, 160, &kk_logo_1_image}, + {136, 9, ATIME, 160, &kk_logo_1_image}, + {138, 9, ATIME, 160, &kk_logo_1_image}, + {140, 9, ATIME, 160, &kk_logo_1_image}, + {142, 9, ATIME, 160, &kk_logo_1_image}, + {144, 9, ATIME, 160, &kk_logo_1_image}, + {146, 9, ATIME, 160, &kk_logo_1_image}, + {148, 9, ATIME, 160, &kk_logo_1_image}, + {150, 9, ATIME, 160, &kk_logo_1_image}, + {152, 9, ATIME, 160, &kk_logo_1_image}, + {154, 9, ATIME, 160, &kk_logo_1_image}, + {156, 9, ATIME, 160, &kk_logo_1_image}, + {158, 9, ATIME, 160, &kk_logo_1_image}, + {160, 9, ATIME, 160, &kk_logo_1_image}, + {162, 9, ATIME, 160, &kk_logo_1_image}, + {164, 9, ATIME, 160, &kk_logo_1_image}, + {166, 9, ATIME, 160, &kk_logo_1_image}, + {168, 9, ATIME, 160, &kk_logo_1_image}, + {170, 9, ATIME, 160, &kk_logo_1_image}, + {172, 9, ATIME, 160, &kk_logo_1_image}, + {174, 9, ATIME, 160, &kk_logo_1_image}, + {176, 9, ATIME, 160, &kk_logo_1_image}, + {178, 9, ATIME, 160, &kk_logo_1_image}, + {180, 9, ATIME, 160, &kk_logo_1_image}, + {182, 9, ATIME, 160, &kk_logo_1_image}, + {184, 9, ATIME, 160, &kk_logo_1_image}, + {186, 9, ATIME, 160, &kk_logo_1_image}, + {188, 9, ATIME, 160, &kk_logo_1_image}, + {190, 9, ATIME, 160, &kk_logo_1_image}, + {192, 9, ATIME, 160, &kk_logo_1_image}, + {194, 9, ATIME, 160, &kk_logo_1_image}, + {196, 9, ATIME, 160, &kk_logo_1_image}, + {198, 9, ATIME, 160, &kk_logo_1_image}, + {198, 9, ATIME, 160, &kk_logo_1_image}, + {196, 9, ATIME, 160, &kk_logo_1_image}, + {194, 9, ATIME, 160, &kk_logo_1_image}, + {192, 9, ATIME, 160, &kk_logo_1_image}, + {190, 9, ATIME, 160, &kk_logo_1_image}, + {188, 9, ATIME, 160, &kk_logo_1_image}, + {186, 9, ATIME, 160, &kk_logo_1_image}, + {184, 9, ATIME, 160, &kk_logo_1_image}, + {182, 9, ATIME, 160, &kk_logo_1_image}, + {180, 9, ATIME, 160, &kk_logo_1_image}, + {178, 9, ATIME, 160, &kk_logo_1_image}, + {176, 9, ATIME, 160, &kk_logo_1_image}, + {174, 9, ATIME, 160, &kk_logo_1_image}, + {172, 9, ATIME, 160, &kk_logo_1_image}, + {170, 9, ATIME, 160, &kk_logo_1_image}, + {168, 9, ATIME, 160, &kk_logo_1_image}, + {166, 9, ATIME, 160, &kk_logo_1_image}, + {164, 9, ATIME, 160, &kk_logo_1_image}, + {162, 9, ATIME, 160, &kk_logo_1_image}, + {160, 9, ATIME, 160, &kk_logo_1_image}, + {158, 9, ATIME, 160, &kk_logo_1_image}, + {156, 9, ATIME, 160, &kk_logo_1_image}, + {154, 9, ATIME, 160, &kk_logo_1_image}, + {152, 9, ATIME, 160, &kk_logo_1_image}, + {150, 9, ATIME, 160, &kk_logo_1_image}, + {148, 9, ATIME, 160, &kk_logo_1_image}, + {146, 9, ATIME, 160, &kk_logo_1_image}, + {144, 9, ATIME, 160, &kk_logo_1_image}, + {142, 9, ATIME, 160, &kk_logo_1_image}, + {140, 9, ATIME, 160, &kk_logo_1_image}, + {138, 9, ATIME, 160, &kk_logo_1_image}, + {136, 9, ATIME, 160, &kk_logo_1_image}, + {134, 9, ATIME, 160, &kk_logo_1_image}, + {132, 9, ATIME, 160, &kk_logo_1_image}, + {130, 9, ATIME, 160, &kk_logo_1_image}, + {128, 9, ATIME, 160, &kk_logo_1_image}, + {126, 9, ATIME, 160, &kk_logo_1_image}, + {124, 9, ATIME, 160, &kk_logo_1_image}, + {122, 9, ATIME, 160, &kk_logo_1_image}, + {120, 9, ATIME, 160, &kk_logo_1_image}, + {118, 9, ATIME, 160, &kk_logo_1_image}, + {116, 9, ATIME, 160, &kk_logo_1_image}, + {114, 9, ATIME, 160, &kk_logo_1_image}, + {112, 9, ATIME, 160, &kk_logo_1_image}, + {110, 9, ATIME, 160, &kk_logo_1_image}, + {108, 9, ATIME, 160, &kk_logo_1_image}, + {106, 9, ATIME, 160, &kk_logo_1_image}, + {104, 9, ATIME, 160, &kk_logo_1_image}, + {102, 9, ATIME, 160, &kk_logo_1_image}, + {100, 9, ATIME, 160, &kk_logo_1_image}, + {98, 9, ATIME, 160, &kk_logo_1_image}, + {96, 9, ATIME, 160, &kk_logo_1_image}, + {94, 9, ATIME, 160, &kk_logo_1_image}, + {92, 9, ATIME, 160, &kk_logo_1_image}, + {90, 9, ATIME, 160, &kk_logo_1_image}, + {88, 9, ATIME, 160, &kk_logo_1_image}, + {86, 9, ATIME, 160, &kk_logo_1_image}, + {84, 9, ATIME, 160, &kk_logo_1_image}, + {82, 9, ATIME, 160, &kk_logo_1_image}, + {80, 9, ATIME, 160, &kk_logo_1_image}, + {78, 9, ATIME, 160, &kk_logo_1_image}, + {76, 9, ATIME, 160, &kk_logo_1_image}, + {74, 9, ATIME, 160, &kk_logo_1_image}, + {72, 9, ATIME, 160, &kk_logo_1_image}, + {70, 9, ATIME, 160, &kk_logo_1_image}, + {68, 9, ATIME, 160, &kk_logo_1_image}, + {66, 9, ATIME, 160, &kk_logo_1_image}, + {64, 9, ATIME, 160, &kk_logo_1_image}, + {62, 9, ATIME, 160, &kk_logo_1_image}, + {60, 9, ATIME, 160, &kk_logo_1_image}, + {58, 9, ATIME, 160, &kk_logo_1_image}, + {56, 9, ATIME, 160, &kk_logo_1_image}, + {54, 9, ATIME, 160, &kk_logo_1_image}, + {52, 9, ATIME, 160, &kk_logo_1_image}, + {50, 9, ATIME, 160, &kk_logo_1_image}, + {48, 9, ATIME, 160, &kk_logo_1_image}, + {46, 9, ATIME, 160, &kk_logo_1_image}, + {44, 9, ATIME, 160, &kk_logo_1_image}, + {42, 9, ATIME, 160, &kk_logo_1_image}, + {40, 9, ATIME, 160, &kk_logo_1_image}, + {38, 9, ATIME, 160, &kk_logo_1_image}, + {36, 9, ATIME, 160, &kk_logo_1_image}, + {34, 9, ATIME, 160, &kk_logo_1_image}, + {32, 9, ATIME, 160, &kk_logo_1_image}, + {30, 9, ATIME, 160, &kk_logo_1_image}, + {28, 9, ATIME, 160, &kk_logo_1_image}, + {26, 9, ATIME, 160, &kk_logo_1_image}, + {24, 9, ATIME, 160, &kk_logo_1_image}, + {22, 9, ATIME, 160, &kk_logo_1_image}, + {20, 9, ATIME, 160, &kk_logo_1_image}, + {18, 9, ATIME, 160, &kk_logo_1_image}, + {16, 9, ATIME, 160, &kk_logo_1_image}, + {14, 9, ATIME, 160, &kk_logo_1_image}, + {12, 9, ATIME, 160, &kk_logo_1_image}, + {10, 9, ATIME, 160, &kk_logo_1_image}, + {8, 9, ATIME, 160, &kk_logo_1_image}, + {6, 9, ATIME, 160, &kk_logo_1_image}, + {4, 9, ATIME, 160, &kk_logo_1_image}, + {2, 9, ATIME, 160, &kk_logo_1_image}, + {0, 9, ATIME, 160, &kk_logo_1_image}, + {0, 9, ATIME, 160, &kk_logo_1_image}, + {2, 9, ATIME, 160, &kk_logo_1_image}, + {4, 9, ATIME, 160, &kk_logo_1_image}, + {6, 9, ATIME, 160, &kk_logo_1_image}, + {8, 9, ATIME, 160, &kk_logo_1_image}, + {10, 9, ATIME, 160, &kk_logo_1_image}, + {12, 9, ATIME, 160, &kk_logo_1_image}, + {14, 9, ATIME, 160, &kk_logo_1_image}, + {16, 9, ATIME, 160, &kk_logo_1_image}, + {18, 9, ATIME, 160, &kk_logo_1_image}, + {20, 9, ATIME, 160, &kk_logo_1_image}, + {22, 9, ATIME, 160, &kk_logo_1_image}, + {24, 9, ATIME, 160, &kk_logo_1_image}, + {26, 9, ATIME, 160, &kk_logo_1_image}, + {28, 9, ATIME, 160, &kk_logo_1_image}, + {30, 9, ATIME, 160, &kk_logo_1_image}, + {32, 9, ATIME, 160, &kk_logo_1_image}, + {34, 9, ATIME, 160, &kk_logo_1_image}, + {36, 9, ATIME, 160, &kk_logo_1_image}, + {38, 9, ATIME, 160, &kk_logo_1_image}, + {40, 9, ATIME, 160, &kk_logo_1_image}, + {42, 9, ATIME, 160, &kk_logo_1_image}, + {44, 9, ATIME, 160, &kk_logo_1_image}, + {46, 9, ATIME, 160, &kk_logo_1_image}, + {48, 9, ATIME, 160, &kk_logo_1_image}, + {50, 9, ATIME, 160, &kk_logo_1_image}, + {52, 9, ATIME, 160, &kk_logo_1_image}, + {54, 9, ATIME, 160, &kk_logo_1_image}, + {56, 9, ATIME, 160, &kk_logo_1_image}, + {58, 9, ATIME, 160, &kk_logo_1_image}, + {60, 9, ATIME, 160, &kk_logo_1_image}, + {62, 9, ATIME, 160, &kk_logo_1_image}, + {64, 9, ATIME, 160, &kk_logo_1_image}, + {66, 9, ATIME, 160, &kk_logo_1_image}, + {68, 9, ATIME, 160, &kk_logo_1_image}, + {70, 9, ATIME, 160, &kk_logo_1_image}, + {72, 9, ATIME, 160, &kk_logo_1_image}, + {74, 9, ATIME, 160, &kk_logo_1_image}, + {76, 9, ATIME, 160, &kk_logo_1_image}, + {78, 9, ATIME, 160, &kk_logo_1_image}, + {80, 9, ATIME, 160, &kk_logo_1_image}, + {82, 9, ATIME, 160, &kk_logo_1_image}, + {84, 9, ATIME, 160, &kk_logo_1_image}, + {86, 9, ATIME, 160, &kk_logo_1_image}, + {88, 9, ATIME, 160, &kk_logo_1_image}, + {90, 9, ATIME, 160, &kk_logo_1_image}, + {92, 9, ATIME, 160, &kk_logo_1_image}, + {94, 9, ATIME, 160, &kk_logo_1_image}, + {96, 9, ATIME, 160, &kk_logo_1_image}, + {98, 9, ATIME, 160, &kk_logo_1_image}, }}; diff --git a/lib/variant/keepkey/logobtc.c b/lib/variant/keepkey/logobtc.c new file mode 100644 index 000000000..478c9c7bb --- /dev/null +++ b/lib/variant/keepkey/logobtc.c @@ -0,0 +1,460 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2025 markrypto + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "keepkey/board/resources.h" + +const uint8_t kkbtc_logo_data[1798] = +{ + 0x16, 0x0, 0xff, 0x43, 0x2, 0xff, 0x7, 0x0, 0x2, 0xff, 0x3, 0x0, 0xff, 0x89, 0x14, 0x0, 0x2, 0xff, 0x1f, 0x0, 0xff, 0x9d, 0x2, 0xff, 0x7, 0x0, 0xfe, 0xff, 0xf0, 0x2, 0x0, 0x2, 0xff, 0x14, 0x0, 0x2, 0xff, 0x1f, 0x0, 0x2, 0xff, 0xfd, 0xef, 0x4f, 0x8c, 0x8, 0x0, 0xff, 0x78, 0x2, 0xff, 0xff, 0x4f, 0x3, 0x0, 0xfd, 0xb4, 0xe4, 0x9d, 0x5, 0x0, 0xfd, 0xb4, 0xe4, 0xb4, 0xc, 0x0, 0xfe, 0xc4, 0xb8, 0x18, 0x0, 0x6, 0xff, 0xff, 0xb8, 0x2, 0x0, 0x2, 0xff, 0xfe, 0x4f, 0x72, 0x4, 0xff, 0xfe, 0x0, 0x78, 0x5, 0xff, 0x2, 0x0, 0xff, 0x92, 0x5, 0xff, 0xff, 0x57, 0x2, 0x0, 0x2, 0xff, 0xfd, 0xb8, 0x0, 0xe4, 0x6, 0xff, 0xfe, 0x0, 0xcf, 0x3, 0xff, 0xff, 0x19, 0x11, 0x0, 0x2, 0xff, 0xfd, 0xf9, 0x0, 0xcf, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0xff, 0xf9, 0x2, 0x0, 0x2, 0xff, 0xfe, 0xbe, 0x4f, 0x2, 0xff, 0xfe, 0x72, 0x0, 0x2, 0xff, 0xfd, 0xdb, 0x0, 0xe4, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0x3, 0xff, 0xfe, 0x78, 0xb8, 0x2, 0xff, 0xff, 0x0, 0x5, 0xff, 0x10, 0x0, 0xff, 0x3c, 0x2, 0xff, 0x3, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0xff, 0xef, 0x2, 0xff, 0x5, 0x0, 0xff, 0xe4, 0x2, 0xff, 0x3, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0xff, 0x48, 0x2, 0x0, 0x2, 0xff, 0xff, 0x0, 0x5, 0xff, 0xff, 0x43, 0xf, 0x0, 0xff, 0xcf, 0x2, 0xff, 0x3, 0x0, 0x2, 0xff, 0xfe, 0x0, 0x6c, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0xff, 0x72, 0x5, 0x0, 0x2, 0xff, 0xff, 0xa9, 0x3, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0xff, 0x78, 0x2, 0xff, 0xfe, 0x0, 0xb8, 0x4, 0xff, 0xff, 0x57, 0xf, 0x0, 0x2, 0xff, 0xff, 0xf9, 0x2, 0x0, 0xff, 0x5e, 0x2, 0xff, 0xfc, 0x0, 0xe4, 0xff, 0xef, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0x6, 0x0, 0x2, 0xff, 0xff, 0xa9, 0x2, 0x0, 0xff, 0xa9, 0x2, 0xff, 0xfa, 0x0, 0xae, 0xff, 0xf9, 0x0, 0x72, 0x2, 0xff, 0x2, 0x0, 0xfd, 0xef, 0xff, 0xef, 0x2, 0x0, 0xff, 0x43, 0x3, 0xff, 0xff, 0x4f, 0xf, 0x0, 0x3, 0xff, 0x2, 0x0, 0x2, 0xff, 0xfe, 0x72, 0x0, 0x2, 0xff, 0xfd, 0x78, 0x0, 0xa9, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0xfe, 0xd8, 0x0, 0x2, 0xff, 0xfd, 0xf0, 0x0, 0xd8, 0x2, 0xff, 0xfe, 0x0, 0x3c, 0x2, 0xff, 0xfe, 0x72, 0x0, 0x2, 0xff, 0xfb, 0xa9, 0x0, 0xef, 0xff, 0xd8, 0x2, 0x0, 0x2, 0xff, 0xff, 0x96, 0x3, 0x0, 0x3, 0xff, 0x10, 0x0, 0x6, 0xff, 0xff, 0xcf, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0xff, 0x9d, 0x3, 0xff, 0x2, 0x0, 0x5, 0xff, 0x3, 0x0, 0x5, 0xff, 0xff, 0xd8, 0x2, 0x0, 0x2, 0xff, 0x2, 0x0, 0x2, 0xff, 0xff, 0x4f, 0x2, 0x0, 0x2, 0xff, 0x3, 0x0, 0xff, 0x19, 0x3, 0xff, 0x10, 0x0, 0xfb, 0xcf, 0x78, 0x0, 0x72, 0xa9, 0x4, 0x0, 0xfe, 0xbe, 0x9d, 0x3, 0x0, 0xfd, 0x96, 0xd4, 0x4f, 0x3, 0x0, 0xfd, 0x78, 0xa9, 0x57, 0x5, 0x0, 0xff, 0x6c, 0x2, 0xbe, 0x4, 0x0, 0xfe, 0x78, 0xa9, 0x2, 0x0, 0xfe, 0x78, 0xa9, 0x3, 0x0, 0xfe, 0xbe, 0x9d, 0x3, 0x0, 0xff, 0x57, 0x3, 0xff, 0x41, 0x0, 0xff, 0x57, 0x3, 0xff, 0x8, 0x0, 0xff, 0x29, 0x2, 0x57, 0x9, 0x0, 0x2, 0xc, 0x10, 0x0, 0xfe, 0x19, 0x36, 0x8, 0x0, 0xfe, 0x57, 0xa9, 0x7, 0x0, 0xfe, 0xa9, 0x5e, 0x6, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x7, 0x0, 0x4, 0xff, 0xff, 0x8c, 0x5, 0x0, 0x8, 0xff, 0xff, 0x4f, 0x8, 0x0, 0xff, 0xa9, 0x7, 0xff, 0xff, 0xdb, 0x4, 0x0, 0xff, 0x57, 0x3, 0xff, 0xff, 0xbe, 0x2, 0x0, 0x7, 0xff, 0xff, 0x5e, 0x3, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x6, 0x0, 0xff, 0xdb, 0x4, 0xff, 0xff, 0x96, 0x3, 0x0, 0xff, 0x72, 0xa, 0xff, 0xff, 0xca, 0x6, 0x0, 0xb, 0xff, 0x4, 0x0, 0x4, 0xff, 0xff, 0xa9, 0x9, 0xff, 0xff, 0xa9, 0x2, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x5, 0x0, 0xff, 0x78, 0x5, 0xff, 0x4, 0x0, 0xc, 0xff, 0x5, 0x0, 0xff, 0xf9, 0xb, 0xff, 0xff, 0xf9, 0x3, 0x0, 0xf, 0xff, 0x2, 0x0, 0xff, 0x57, 0x3, 0xff, 0x4, 0x0, 0xff, 0x5e, 0x4, 0xff, 0x5, 0x0, 0x4, 0xff, 0xff, 0xf0, 0x5, 0x0, 0x4, 0xff, 0x4, 0x0, 0x4, 0xff, 0xff, 0x5e, 0x4, 0x0, 0xff, 0xe4, 0x3, 0xff, 0x4, 0x0, 0x5, 0xff, 0x5, 0x0, 0x4, 0xff, 0xfd, 0x96, 0x0, 0x57, 0x3, 0xff, 0x4, 0x0, 0x4, 0xff, 0xff, 0x43, 0x5, 0x0, 0x3, 0xff, 0xff, 0xf0, 0x7, 0x0, 0x3, 0xff, 0x3, 0x0, 0x4, 0xff, 0x7, 0x0, 0x3, 0xff, 0xff, 0xb4, 0x3, 0x0, 0x4, 0xff, 0x7, 0x0, 0x3, 0xff, 0xfd, 0xef, 0x0, 0x48, 0x3, 0xff, 0xff, 0x81, 0x2, 0x0, 0x4, 0xff, 0xff, 0xa9, 0x5, 0x0, 0xff, 0xa9, 0x3, 0xff, 0x8, 0x0, 0x3, 0xff, 0x3, 0x0, 0x3, 0xff, 0xff, 0x81, 0x7, 0x0, 0xff, 0xa9, 0x3, 0xff, 0x3, 0x0, 0x3, 0xff, 0xff, 0xe4, 0x7, 0x0, 0x3, 0xff, 0xfd, 0xf9, 0x0, 0x1b, 0x9, 0xff, 0xff, 0x8c, 0x6, 0x0, 0xff, 0xef, 0x3, 0xff, 0x8, 0x0, 0x3, 0xff, 0x3, 0x0, 0x3, 0xff, 0xff, 0x81, 0x7, 0x0, 0xff, 0xe4, 0x3, 0xff, 0x3, 0x0, 0x3, 0xff, 0xff, 0xd4, 0x7, 0x0, 0x3, 0xff, 0xfd, 0xf9, 0x0, 0x1b, 0x8, 0xff, 0xff, 0xf0, 0x7, 0x0, 0xf, 0xff, 0xff, 0x72, 0x2, 0x0, 0xf, 0xff, 0x3, 0x0, 0x3, 0xff, 0xff, 0xd4, 0x7, 0x0, 0x3, 0xff, 0xfd, 0xf9, 0x0, 0x1b, 0x9, 0xff, 0x7, 0x0, 0xf, 0xff, 0xff, 0x5e, 0x2, 0x0, 0xf, 0xff, 0x3, 0x0, 0x3, 0xff, 0xff, 0xd4, 0x7, 0x0, 0x3, 0xff, 0xfd, 0xf9, 0x0, 0x1b, 0x4, 0xff, 0xfe, 0xd4, 0xe4, 0x4, 0xff, 0x6, 0x0, 0x4, 0xff, 0x4, 0x0, 0xfe, 0x30, 0x48, 0x4, 0x5e, 0xff, 0x43, 0x3, 0x0, 0x3, 0xff, 0xff, 0x81, 0x3, 0x0, 0xfe, 0x29, 0x43, 0x4, 0x5e, 0xff, 0x72, 0x4, 0x0, 0x3, 0xff, 0xff, 0xd4, 0x7, 0x0, 0x3, 0xff, 0xfd, 0xf9, 0x0, 0x57, 0x3, 0xff, 0x3, 0x0, 0xff, 0x9d, 0x3, 0xff, 0xff, 0xf9, 0x5, 0x0, 0xff, 0xca, 0x3, 0xff, 0xe, 0x0, 0x3, 0xff, 0xff, 0x43, 0xe, 0x0, 0x3, 0xff, 0xff, 0xd8, 0x7, 0x0, 0x4, 0xff, 0xfe, 0x0, 0x57, 0x3, 0xff, 0x4, 0x0, 0x4, 0xff, 0xff, 0xa9, 0x4, 0x0, 0xff, 0x36, 0x3, 0xff, 0xff, 0xa9, 0xd, 0x0, 0x4, 0xff, 0xe, 0x0, 0x4, 0xff, 0x7, 0x0, 0x3, 0xff, 0xfd, 0xef, 0x0, 0x5e, 0x3, 0xff, 0x5, 0x0, 0x4, 0xff, 0x5, 0x0, 0x4, 0xff, 0xff, 0xa9, 0x5, 0x0, 0xff, 0x72, 0x2, 0xff, 0x4, 0x0, 0xff, 0xcf, 0x4, 0xff, 0x6, 0x0, 0x2, 0xff, 0x5, 0x0, 0x4, 0xff, 0xff, 0xf0, 0x5, 0x0, 0xff, 0xf0, 0x3, 0xff, 0xfd, 0x89, 0x0, 0x5e, 0x3, 0xff, 0x6, 0x0, 0x5, 0xff, 0x3, 0x0, 0xff, 0xae, 0xc, 0xff, 0xff, 0x9d, 0x4, 0x0, 0xd, 0xff, 0x4, 0x0, 0xe, 0xff, 0x2, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x6, 0x0, 0xff, 0x9d, 0x4, 0xff, 0xff, 0x96, 0x3, 0x0, 0xc, 0xff, 0xff, 0xc4, 0x4, 0x0, 0xff, 0x78, 0xc, 0xff, 0x4, 0x0, 0xd, 0xff, 0xff, 0xae, 0x2, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x7, 0x0, 0x4, 0xff, 0xff, 0x96, 0x4, 0x0, 0xff, 0xef, 0x8, 0xff, 0xff, 0xd4, 0x7, 0x0, 0xff, 0x57, 0x9, 0xff, 0xff, 0x57, 0x5, 0x0, 0x3, 0xff, 0xfe, 0xc4, 0x0, 0x7, 0xff, 0xff, 0x78, 0x3, 0x0, 0xff, 0x72, 0x3, 0xff, 0x8, 0x0, 0xff, 0x89, 0x2, 0xa9, 0x7, 0x0, 0xfb, 0x8c, 0xf9, 0xff, 0xef, 0x96, 0xc, 0x0, 0xfe, 0x3c, 0xe4, 0x2, 0xff, 0xfe, 0xca, 0x4f, 0x8, 0x0, 0x3, 0xff, 0xff, 0xbe, 0x3, 0x0, 0xfc, 0xa9, 0xff, 0xca, 0x4f, 0x5, 0x0, 0xff, 0x72, 0x3, 0xff, 0x31, 0x0, 0x3, 0xff, 0xff, 0xcf, 0xc, 0x0, 0xff, 0x72, 0x3, 0xff, 0x31, 0x0, 0x3, 0xff, 0xff, 0xd4, 0xc, 0x0, 0xff, 0x72, 0x3, 0xff, 0x31, 0x0, 0x3, 0xff, 0xff, 0xe4, 0xc, 0x0, 0xff, 0x72, 0x3, 0xff, 0x31, 0x0, 0x3, 0xff, 0xff, 0xef, 0xc, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x8, 0x0, 0x2, 0xd8, 0xff, 0xe4, 0x8, 0x0, 0xfb, 0x29, 0xa9, 0xd8, 0xbe, 0x3c, 0x6, 0x0, 0xff, 0x89, 0x3, 0x96, 0xff, 0x8c, 0x6, 0x0, 0xff, 0x89, 0x3, 0x96, 0xff, 0x72, 0x3, 0x0, 0x4, 0xff, 0xc, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x7, 0x0, 0x4, 0xff, 0xff, 0xb4, 0x5, 0x0, 0x8, 0xff, 0xff, 0xd8, 0x4, 0x0, 0x5, 0xff, 0x6, 0x0, 0x5, 0xff, 0x3, 0x0, 0x4, 0xff, 0xc, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x6, 0x0, 0x5, 0xff, 0xff, 0x96, 0x4, 0x0, 0xb, 0xff, 0x3, 0x0, 0x5, 0xff, 0x6, 0x0, 0x5, 0xff, 0x3, 0x0, 0x4, 0xff, 0xc, 0x0, 0xff, 0x5e, 0x3, 0xff, 0x5, 0x0, 0xff, 0xef, 0x4, 0xff, 0xff, 0xd4, 0x4, 0x0, 0xc, 0xff, 0xff, 0xd4, 0x2, 0x0, 0xff, 0xa9, 0x4, 0xff, 0x6, 0x0, 0xff, 0xc4, 0x4, 0xff, 0x3, 0x0, 0xfc, 0x9d, 0xf0, 0xef, 0x81, 0xc, 0x0, 0xff, 0x57, 0x3, 0xff, 0x4, 0x0, 0xff, 0xb8, 0x4, 0xff, 0x5, 0x0, 0xff, 0xc4, 0x4, 0xff, 0x5, 0x0, 0xff, 0xf9, 0x3, 0xff, 0x3, 0x0, 0xff, 0xb8, 0x3, 0xff, 0x7, 0x0, 0xff, 0xca, 0x3, 0xff, 0x13, 0x0, 0xff, 0x57, 0x3, 0xff, 0x4, 0x0, 0x4, 0xff, 0x6, 0x0, 0x4, 0xff, 0x7, 0x0, 0x3, 0xff, 0x3, 0x0, 0xff, 0xb8, 0x3, 0xff, 0x7, 0x0, 0xff, 0xc4, 0x3, 0xff, 0x13, 0x0, 0xff, 0x48, 0x3, 0xff, 0xfd, 0xf0, 0xca, 0xe4, 0x4, 0xff, 0x7, 0x0, 0x3, 0xff, 0x8, 0x0, 0xff, 0xf0, 0x2, 0xff, 0xff, 0xf9, 0x2, 0x0, 0xff, 0xb8, 0x3, 0xff, 0x7, 0x0, 0xff, 0xc4, 0x3, 0xff, 0x13, 0x0, 0xff, 0x1b, 0x9, 0xff, 0x7, 0x0, 0xff, 0x72, 0x3, 0xff, 0xff, 0x43, 0x7, 0x0, 0x4, 0xff, 0x2, 0x0, 0xff, 0xb8, 0x3, 0xff, 0x7, 0x0, 0xff, 0xc4, 0x3, 0xff, 0x13, 0x0, 0xff, 0x1b, 0x8, 0xff, 0xff, 0xf0, 0x7, 0x0, 0xff, 0xae, 0xf, 0xff, 0x2, 0x0, 0xff, 0xb8, 0x3, 0xff, 0x7, 0x0, 0xff, 0xc4, 0x3, 0xff, 0x13, 0x0, 0xff, 0x1b, 0x9, 0xff, 0x7, 0x0, 0xff, 0xae, 0xe, 0xff, 0xff, 0xf9, 0x2, 0x0, 0xff, 0xb8, 0x3, 0xff, 0x7, 0x0, 0xff, 0xc4, 0x3, 0xff, 0x13, 0x0, 0xff, 0x57, 0x3, 0xff, 0xff, 0x9d, 0x2, 0x0, 0x4, 0xff, 0x6, 0x0, 0xff, 0xa9, 0x3, 0xff, 0xe, 0x0, 0xff, 0xb8, 0x3, 0xff, 0x7, 0x0, 0xff, 0xb4, 0x3, 0xff, 0x13, 0x0, 0xff, 0x57, 0x3, 0xff, 0x4, 0x0, 0x4, 0xff, 0x6, 0x0, 0x3, 0xff, 0xe, 0x0, 0xff, 0xc4, 0x3, 0xff, 0x7, 0x0, 0xff, 0xb8, 0x3, 0xff, 0x13, 0x0, 0xff, 0x72, 0x3, 0xff, 0x4, 0x0, 0xff, 0xa9, 0x4, 0xff, 0x5, 0x0, 0x4, 0xff, 0xd, 0x0, 0xff, 0x78, 0x3, 0xff, 0xff, 0xa9, 0x6, 0x0, 0x4, 0xff, 0x13, 0x0, 0xff, 0x72, 0x3, 0xff, 0x5, 0x0, 0x4, 0xff, 0xff, 0xb8, 0x4, 0x0, 0xff, 0xf9, 0x4, 0xff, 0x5, 0x0, 0xff, 0x72, 0x2, 0xff, 0x5, 0x0, 0x4, 0xff, 0xff, 0xb8, 0x3, 0x0, 0xff, 0x6c, 0x5, 0xff, 0x13, 0x0, 0xff, 0x92, 0x3, 0xff, 0x6, 0x0, 0x5, 0xff, 0xff, 0x57, 0x3, 0x0, 0xd, 0xff, 0x4, 0x0, 0xe, 0xff, 0x13, 0x0, 0xff, 0xa9, 0x3, 0xff, 0xff, 0x5e, 0x6, 0x0, 0x4, 0xff, 0xff, 0x96, 0x3, 0x0, 0xff, 0xa9, 0xb, 0xff, 0xff, 0xe4, 0x5, 0x0, 0x9, 0xff, 0xff, 0xe4, 0x3, 0xff, 0x13, 0x0, 0xff, 0xa9, 0x3, 0xff, 0xff, 0x81, 0x6, 0x0, 0xff, 0xc4, 0x3, 0xff, 0xff, 0xa9, 0x4, 0x0, 0xff, 0x81, 0x8, 0xff, 0xff, 0xef, 0x8, 0x0, 0xff, 0xe4, 0x5, 0xff, 0xfd, 0x92, 0x0, 0x9d, 0x3, 0xff, 0x14, 0x0, 0xff, 0x29, 0x14, 0x0, 0xfb, 0x57, 0x9d, 0xd4, 0xc4, 0x96, 0x13, 0x0, 0xff, 0x9d, 0x3, 0xff, 0x41, 0x0, 0xff, 0xb4, 0x3, 0xff, 0x41, 0x0, 0xff, 0xa9, 0x3, 0xff, 0x41, 0x0, 0xff, 0xa9, 0x3, 0xff, 0x41, 0x0, 0xff, 0xca, 0x3, 0xff, 0x40, 0x0, 0xff, 0xef, 0x4, 0xff, 0x40, 0x0, 0x5, 0xff, 0x40, 0x0, 0x5, 0xff, 0x40, 0x0, 0xff, 0xb8, 0x2, 0xff, 0xff, 0xc4, 0x13, 0x0 +}; +static const Image kkbtc_logo_image = {69, 60, 1798, kkbtc_logo_data}; + +const VariantAnimation kkbtc_logo = { + 21, + { + {94, 2, 25, 0, &kkbtc_logo_image}, + {94, 2, 25, 5, &kkbtc_logo_image}, + {94, 2, 25, 10, &kkbtc_logo_image}, + {94, 2, 25, 15, &kkbtc_logo_image}, + {94, 2, 25, 20, &kkbtc_logo_image}, + {94, 2, 25, 25, &kkbtc_logo_image}, + {94, 2, 25, 30, &kkbtc_logo_image}, + {94, 2, 25, 35, &kkbtc_logo_image}, + {94, 2, 25, 40, &kkbtc_logo_image}, + {94, 2, 25, 45, &kkbtc_logo_image}, + {94, 2, 25, 50, &kkbtc_logo_image}, + {94, 2, 25, 55, &kkbtc_logo_image}, + {94, 2, 25, 60, &kkbtc_logo_image}, + {94, 2, 25, 65, &kkbtc_logo_image}, + {94, 2, 25, 70, &kkbtc_logo_image}, + {94, 2, 25, 75, &kkbtc_logo_image}, + {94, 2, 25, 80, &kkbtc_logo_image}, + {94, 2, 25, 85, &kkbtc_logo_image}, + {94, 2, 25, 90, &kkbtc_logo_image}, + {94, 2, 25, 95, &kkbtc_logo_image}, + {94, 2, 25, 100, &kkbtc_logo_image}, + } +}; +const VariantAnimation kkbtc_logo_reversed = { + 21, + { + {94, 2, 25, 100, &kkbtc_logo_image}, + {94, 2, 25, 95, &kkbtc_logo_image}, + {94, 2, 25, 90, &kkbtc_logo_image}, + {94, 2, 25, 85, &kkbtc_logo_image}, + {94, 2, 25, 80, &kkbtc_logo_image}, + {94, 2, 25, 75, &kkbtc_logo_image}, + {94, 2, 25, 70, &kkbtc_logo_image}, + {94, 2, 25, 65, &kkbtc_logo_image}, + {94, 2, 25, 60, &kkbtc_logo_image}, + {94, 2, 25, 55, &kkbtc_logo_image}, + {94, 2, 25, 50, &kkbtc_logo_image}, + {94, 2, 25, 45, &kkbtc_logo_image}, + {94, 2, 25, 40, &kkbtc_logo_image}, + {94, 2, 25, 35, &kkbtc_logo_image}, + {94, 2, 25, 30, &kkbtc_logo_image}, + {94, 2, 25, 25, &kkbtc_logo_image}, + {94, 2, 25, 20, &kkbtc_logo_image}, + {94, 2, 25, 15, &kkbtc_logo_image}, + {94, 2, 25, 10, &kkbtc_logo_image}, + {94, 2, 25, 5, &kkbtc_logo_image}, + {94, 2, 25, 0, &kkbtc_logo_image}, + } +}; + +const VariantAnimation kkbtc_screensaver = { + 374, + { + {94, 2, 75, 60, &kkbtc_logo_image}, + {95, 2, 75, 60, &kkbtc_logo_image}, + {96, 2, 75, 60, &kkbtc_logo_image}, + {97, 2, 75, 60, &kkbtc_logo_image}, + {98, 2, 75, 60, &kkbtc_logo_image}, + {99, 2, 75, 60, &kkbtc_logo_image}, + {100, 2, 75, 60, &kkbtc_logo_image}, + {101, 2, 75, 60, &kkbtc_logo_image}, + {102, 2, 75, 60, &kkbtc_logo_image}, + {103, 2, 75, 60, &kkbtc_logo_image}, + {104, 2, 75, 60, &kkbtc_logo_image}, + {105, 2, 75, 60, &kkbtc_logo_image}, + {106, 2, 75, 60, &kkbtc_logo_image}, + {107, 2, 75, 60, &kkbtc_logo_image}, + {108, 2, 75, 60, &kkbtc_logo_image}, + {109, 2, 75, 60, &kkbtc_logo_image}, + {110, 2, 75, 60, &kkbtc_logo_image}, + {111, 2, 75, 60, &kkbtc_logo_image}, + {112, 2, 75, 60, &kkbtc_logo_image}, + {113, 2, 75, 60, &kkbtc_logo_image}, + {114, 2, 75, 60, &kkbtc_logo_image}, + {115, 2, 75, 60, &kkbtc_logo_image}, + {116, 2, 75, 60, &kkbtc_logo_image}, + {117, 2, 75, 60, &kkbtc_logo_image}, + {118, 2, 75, 60, &kkbtc_logo_image}, + {119, 2, 75, 60, &kkbtc_logo_image}, + {120, 2, 75, 60, &kkbtc_logo_image}, + {121, 2, 75, 60, &kkbtc_logo_image}, + {122, 2, 75, 60, &kkbtc_logo_image}, + {123, 2, 75, 60, &kkbtc_logo_image}, + {124, 2, 75, 60, &kkbtc_logo_image}, + {125, 2, 75, 60, &kkbtc_logo_image}, + {126, 2, 75, 60, &kkbtc_logo_image}, + {127, 2, 75, 60, &kkbtc_logo_image}, + {128, 2, 75, 60, &kkbtc_logo_image}, + {129, 2, 75, 60, &kkbtc_logo_image}, + {130, 2, 75, 60, &kkbtc_logo_image}, + {131, 2, 75, 60, &kkbtc_logo_image}, + {132, 2, 75, 60, &kkbtc_logo_image}, + {133, 2, 75, 60, &kkbtc_logo_image}, + {134, 2, 75, 60, &kkbtc_logo_image}, + {135, 2, 75, 60, &kkbtc_logo_image}, + {136, 2, 75, 60, &kkbtc_logo_image}, + {137, 2, 75, 60, &kkbtc_logo_image}, + {138, 2, 75, 60, &kkbtc_logo_image}, + {139, 2, 75, 60, &kkbtc_logo_image}, + {140, 2, 75, 60, &kkbtc_logo_image}, + {141, 2, 75, 60, &kkbtc_logo_image}, + {142, 2, 75, 60, &kkbtc_logo_image}, + {143, 2, 75, 60, &kkbtc_logo_image}, + {144, 2, 75, 60, &kkbtc_logo_image}, + {145, 2, 75, 60, &kkbtc_logo_image}, + {146, 2, 75, 60, &kkbtc_logo_image}, + {147, 2, 75, 60, &kkbtc_logo_image}, + {148, 2, 75, 60, &kkbtc_logo_image}, + {149, 2, 75, 60, &kkbtc_logo_image}, + {150, 2, 75, 60, &kkbtc_logo_image}, + {151, 2, 75, 60, &kkbtc_logo_image}, + {152, 2, 75, 60, &kkbtc_logo_image}, + {153, 2, 75, 60, &kkbtc_logo_image}, + {154, 2, 75, 60, &kkbtc_logo_image}, + {155, 2, 75, 60, &kkbtc_logo_image}, + {156, 2, 75, 60, &kkbtc_logo_image}, + {157, 2, 75, 60, &kkbtc_logo_image}, + {158, 2, 75, 60, &kkbtc_logo_image}, + {159, 2, 75, 60, &kkbtc_logo_image}, + {160, 2, 75, 60, &kkbtc_logo_image}, + {161, 2, 75, 60, &kkbtc_logo_image}, + {162, 2, 75, 60, &kkbtc_logo_image}, + {163, 2, 75, 60, &kkbtc_logo_image}, + {164, 2, 75, 60, &kkbtc_logo_image}, + {165, 2, 75, 60, &kkbtc_logo_image}, + {166, 2, 75, 60, &kkbtc_logo_image}, + {167, 2, 75, 60, &kkbtc_logo_image}, + {168, 2, 75, 60, &kkbtc_logo_image}, + {169, 2, 75, 60, &kkbtc_logo_image}, + {170, 2, 75, 60, &kkbtc_logo_image}, + {171, 2, 75, 60, &kkbtc_logo_image}, + {172, 2, 75, 60, &kkbtc_logo_image}, + {173, 2, 75, 60, &kkbtc_logo_image}, + {174, 2, 75, 60, &kkbtc_logo_image}, + {175, 2, 75, 60, &kkbtc_logo_image}, + {176, 2, 75, 60, &kkbtc_logo_image}, + {177, 2, 75, 60, &kkbtc_logo_image}, + {178, 2, 75, 60, &kkbtc_logo_image}, + {179, 2, 75, 60, &kkbtc_logo_image}, + {180, 2, 75, 60, &kkbtc_logo_image}, + {181, 2, 75, 60, &kkbtc_logo_image}, + {182, 2, 75, 60, &kkbtc_logo_image}, + {183, 2, 75, 60, &kkbtc_logo_image}, + {184, 2, 75, 60, &kkbtc_logo_image}, + {185, 2, 75, 60, &kkbtc_logo_image}, + {186, 2, 75, 60, &kkbtc_logo_image}, + {185, 2, 75, 60, &kkbtc_logo_image}, + {184, 2, 75, 60, &kkbtc_logo_image}, + {183, 2, 75, 60, &kkbtc_logo_image}, + {182, 2, 75, 60, &kkbtc_logo_image}, + {181, 2, 75, 60, &kkbtc_logo_image}, + {180, 2, 75, 60, &kkbtc_logo_image}, + {179, 2, 75, 60, &kkbtc_logo_image}, + {178, 2, 75, 60, &kkbtc_logo_image}, + {177, 2, 75, 60, &kkbtc_logo_image}, + {176, 2, 75, 60, &kkbtc_logo_image}, + {175, 2, 75, 60, &kkbtc_logo_image}, + {174, 2, 75, 60, &kkbtc_logo_image}, + {173, 2, 75, 60, &kkbtc_logo_image}, + {172, 2, 75, 60, &kkbtc_logo_image}, + {171, 2, 75, 60, &kkbtc_logo_image}, + {170, 2, 75, 60, &kkbtc_logo_image}, + {169, 2, 75, 60, &kkbtc_logo_image}, + {168, 2, 75, 60, &kkbtc_logo_image}, + {167, 2, 75, 60, &kkbtc_logo_image}, + {166, 2, 75, 60, &kkbtc_logo_image}, + {165, 2, 75, 60, &kkbtc_logo_image}, + {164, 2, 75, 60, &kkbtc_logo_image}, + {163, 2, 75, 60, &kkbtc_logo_image}, + {162, 2, 75, 60, &kkbtc_logo_image}, + {161, 2, 75, 60, &kkbtc_logo_image}, + {160, 2, 75, 60, &kkbtc_logo_image}, + {159, 2, 75, 60, &kkbtc_logo_image}, + {158, 2, 75, 60, &kkbtc_logo_image}, + {157, 2, 75, 60, &kkbtc_logo_image}, + {156, 2, 75, 60, &kkbtc_logo_image}, + {155, 2, 75, 60, &kkbtc_logo_image}, + {154, 2, 75, 60, &kkbtc_logo_image}, + {153, 2, 75, 60, &kkbtc_logo_image}, + {152, 2, 75, 60, &kkbtc_logo_image}, + {151, 2, 75, 60, &kkbtc_logo_image}, + {150, 2, 75, 60, &kkbtc_logo_image}, + {149, 2, 75, 60, &kkbtc_logo_image}, + {148, 2, 75, 60, &kkbtc_logo_image}, + {147, 2, 75, 60, &kkbtc_logo_image}, + {146, 2, 75, 60, &kkbtc_logo_image}, + {145, 2, 75, 60, &kkbtc_logo_image}, + {144, 2, 75, 60, &kkbtc_logo_image}, + {143, 2, 75, 60, &kkbtc_logo_image}, + {142, 2, 75, 60, &kkbtc_logo_image}, + {141, 2, 75, 60, &kkbtc_logo_image}, + {140, 2, 75, 60, &kkbtc_logo_image}, + {139, 2, 75, 60, &kkbtc_logo_image}, + {138, 2, 75, 60, &kkbtc_logo_image}, + {137, 2, 75, 60, &kkbtc_logo_image}, + {136, 2, 75, 60, &kkbtc_logo_image}, + {135, 2, 75, 60, &kkbtc_logo_image}, + {134, 2, 75, 60, &kkbtc_logo_image}, + {133, 2, 75, 60, &kkbtc_logo_image}, + {132, 2, 75, 60, &kkbtc_logo_image}, + {131, 2, 75, 60, &kkbtc_logo_image}, + {130, 2, 75, 60, &kkbtc_logo_image}, + {129, 2, 75, 60, &kkbtc_logo_image}, + {128, 2, 75, 60, &kkbtc_logo_image}, + {127, 2, 75, 60, &kkbtc_logo_image}, + {126, 2, 75, 60, &kkbtc_logo_image}, + {125, 2, 75, 60, &kkbtc_logo_image}, + {124, 2, 75, 60, &kkbtc_logo_image}, + {123, 2, 75, 60, &kkbtc_logo_image}, + {122, 2, 75, 60, &kkbtc_logo_image}, + {121, 2, 75, 60, &kkbtc_logo_image}, + {120, 2, 75, 60, &kkbtc_logo_image}, + {119, 2, 75, 60, &kkbtc_logo_image}, + {118, 2, 75, 60, &kkbtc_logo_image}, + {117, 2, 75, 60, &kkbtc_logo_image}, + {116, 2, 75, 60, &kkbtc_logo_image}, + {115, 2, 75, 60, &kkbtc_logo_image}, + {114, 2, 75, 60, &kkbtc_logo_image}, + {113, 2, 75, 60, &kkbtc_logo_image}, + {112, 2, 75, 60, &kkbtc_logo_image}, + {111, 2, 75, 60, &kkbtc_logo_image}, + {110, 2, 75, 60, &kkbtc_logo_image}, + {109, 2, 75, 60, &kkbtc_logo_image}, + {108, 2, 75, 60, &kkbtc_logo_image}, + {107, 2, 75, 60, &kkbtc_logo_image}, + {106, 2, 75, 60, &kkbtc_logo_image}, + {105, 2, 75, 60, &kkbtc_logo_image}, + {104, 2, 75, 60, &kkbtc_logo_image}, + {103, 2, 75, 60, &kkbtc_logo_image}, + {102, 2, 75, 60, &kkbtc_logo_image}, + {101, 2, 75, 60, &kkbtc_logo_image}, + {100, 2, 75, 60, &kkbtc_logo_image}, + {99, 2, 75, 60, &kkbtc_logo_image}, + {98, 2, 75, 60, &kkbtc_logo_image}, + {97, 2, 75, 60, &kkbtc_logo_image}, + {96, 2, 75, 60, &kkbtc_logo_image}, + {95, 2, 75, 60, &kkbtc_logo_image}, + {94, 2, 75, 60, &kkbtc_logo_image}, + {93, 2, 75, 60, &kkbtc_logo_image}, + {92, 2, 75, 60, &kkbtc_logo_image}, + {91, 2, 75, 60, &kkbtc_logo_image}, + {90, 2, 75, 60, &kkbtc_logo_image}, + {89, 2, 75, 60, &kkbtc_logo_image}, + {88, 2, 75, 60, &kkbtc_logo_image}, + {87, 2, 75, 60, &kkbtc_logo_image}, + {86, 2, 75, 60, &kkbtc_logo_image}, + {85, 2, 75, 60, &kkbtc_logo_image}, + {84, 2, 75, 60, &kkbtc_logo_image}, + {83, 2, 75, 60, &kkbtc_logo_image}, + {82, 2, 75, 60, &kkbtc_logo_image}, + {81, 2, 75, 60, &kkbtc_logo_image}, + {80, 2, 75, 60, &kkbtc_logo_image}, + {79, 2, 75, 60, &kkbtc_logo_image}, + {78, 2, 75, 60, &kkbtc_logo_image}, + {77, 2, 75, 60, &kkbtc_logo_image}, + {76, 2, 75, 60, &kkbtc_logo_image}, + {75, 2, 75, 60, &kkbtc_logo_image}, + {74, 2, 75, 60, &kkbtc_logo_image}, + {73, 2, 75, 60, &kkbtc_logo_image}, + {72, 2, 75, 60, &kkbtc_logo_image}, + {71, 2, 75, 60, &kkbtc_logo_image}, + {70, 2, 75, 60, &kkbtc_logo_image}, + {69, 2, 75, 60, &kkbtc_logo_image}, + {68, 2, 75, 60, &kkbtc_logo_image}, + {67, 2, 75, 60, &kkbtc_logo_image}, + {66, 2, 75, 60, &kkbtc_logo_image}, + {65, 2, 75, 60, &kkbtc_logo_image}, + {64, 2, 75, 60, &kkbtc_logo_image}, + {63, 2, 75, 60, &kkbtc_logo_image}, + {62, 2, 75, 60, &kkbtc_logo_image}, + {61, 2, 75, 60, &kkbtc_logo_image}, + {60, 2, 75, 60, &kkbtc_logo_image}, + {59, 2, 75, 60, &kkbtc_logo_image}, + {58, 2, 75, 60, &kkbtc_logo_image}, + {57, 2, 75, 60, &kkbtc_logo_image}, + {56, 2, 75, 60, &kkbtc_logo_image}, + {55, 2, 75, 60, &kkbtc_logo_image}, + {54, 2, 75, 60, &kkbtc_logo_image}, + {53, 2, 75, 60, &kkbtc_logo_image}, + {52, 2, 75, 60, &kkbtc_logo_image}, + {51, 2, 75, 60, &kkbtc_logo_image}, + {50, 2, 75, 60, &kkbtc_logo_image}, + {49, 2, 75, 60, &kkbtc_logo_image}, + {48, 2, 75, 60, &kkbtc_logo_image}, + {47, 2, 75, 60, &kkbtc_logo_image}, + {46, 2, 75, 60, &kkbtc_logo_image}, + {45, 2, 75, 60, &kkbtc_logo_image}, + {44, 2, 75, 60, &kkbtc_logo_image}, + {43, 2, 75, 60, &kkbtc_logo_image}, + {42, 2, 75, 60, &kkbtc_logo_image}, + {41, 2, 75, 60, &kkbtc_logo_image}, + {40, 2, 75, 60, &kkbtc_logo_image}, + {39, 2, 75, 60, &kkbtc_logo_image}, + {38, 2, 75, 60, &kkbtc_logo_image}, + {37, 2, 75, 60, &kkbtc_logo_image}, + {36, 2, 75, 60, &kkbtc_logo_image}, + {35, 2, 75, 60, &kkbtc_logo_image}, + {34, 2, 75, 60, &kkbtc_logo_image}, + {33, 2, 75, 60, &kkbtc_logo_image}, + {32, 2, 75, 60, &kkbtc_logo_image}, + {31, 2, 75, 60, &kkbtc_logo_image}, + {30, 2, 75, 60, &kkbtc_logo_image}, + {29, 2, 75, 60, &kkbtc_logo_image}, + {28, 2, 75, 60, &kkbtc_logo_image}, + {27, 2, 75, 60, &kkbtc_logo_image}, + {26, 2, 75, 60, &kkbtc_logo_image}, + {25, 2, 75, 60, &kkbtc_logo_image}, + {24, 2, 75, 60, &kkbtc_logo_image}, + {23, 2, 75, 60, &kkbtc_logo_image}, + {22, 2, 75, 60, &kkbtc_logo_image}, + {21, 2, 75, 60, &kkbtc_logo_image}, + {20, 2, 75, 60, &kkbtc_logo_image}, + {19, 2, 75, 60, &kkbtc_logo_image}, + {18, 2, 75, 60, &kkbtc_logo_image}, + {17, 2, 75, 60, &kkbtc_logo_image}, + {16, 2, 75, 60, &kkbtc_logo_image}, + {15, 2, 75, 60, &kkbtc_logo_image}, + {14, 2, 75, 60, &kkbtc_logo_image}, + {13, 2, 75, 60, &kkbtc_logo_image}, + {12, 2, 75, 60, &kkbtc_logo_image}, + {11, 2, 75, 60, &kkbtc_logo_image}, + {10, 2, 75, 60, &kkbtc_logo_image}, + {9, 2, 75, 60, &kkbtc_logo_image}, + {8, 2, 75, 60, &kkbtc_logo_image}, + {7, 2, 75, 60, &kkbtc_logo_image}, + {6, 2, 75, 60, &kkbtc_logo_image}, + {5, 2, 75, 60, &kkbtc_logo_image}, + {4, 2, 75, 60, &kkbtc_logo_image}, + {3, 2, 75, 60, &kkbtc_logo_image}, + {2, 2, 75, 60, &kkbtc_logo_image}, + {1, 2, 75, 60, &kkbtc_logo_image}, + {0, 2, 75, 60, &kkbtc_logo_image}, + {0, 2, 75, 60, &kkbtc_logo_image}, + {1, 2, 75, 60, &kkbtc_logo_image}, + {2, 2, 75, 60, &kkbtc_logo_image}, + {3, 2, 75, 60, &kkbtc_logo_image}, + {4, 2, 75, 60, &kkbtc_logo_image}, + {5, 2, 75, 60, &kkbtc_logo_image}, + {6, 2, 75, 60, &kkbtc_logo_image}, + {7, 2, 75, 60, &kkbtc_logo_image}, + {8, 2, 75, 60, &kkbtc_logo_image}, + {9, 2, 75, 60, &kkbtc_logo_image}, + {10, 2, 75, 60, &kkbtc_logo_image}, + {11, 2, 75, 60, &kkbtc_logo_image}, + {12, 2, 75, 60, &kkbtc_logo_image}, + {13, 2, 75, 60, &kkbtc_logo_image}, + {14, 2, 75, 60, &kkbtc_logo_image}, + {15, 2, 75, 60, &kkbtc_logo_image}, + {16, 2, 75, 60, &kkbtc_logo_image}, + {17, 2, 75, 60, &kkbtc_logo_image}, + {18, 2, 75, 60, &kkbtc_logo_image}, + {19, 2, 75, 60, &kkbtc_logo_image}, + {20, 2, 75, 60, &kkbtc_logo_image}, + {21, 2, 75, 60, &kkbtc_logo_image}, + {22, 2, 75, 60, &kkbtc_logo_image}, + {23, 2, 75, 60, &kkbtc_logo_image}, + {24, 2, 75, 60, &kkbtc_logo_image}, + {25, 2, 75, 60, &kkbtc_logo_image}, + {26, 2, 75, 60, &kkbtc_logo_image}, + {27, 2, 75, 60, &kkbtc_logo_image}, + {28, 2, 75, 60, &kkbtc_logo_image}, + {29, 2, 75, 60, &kkbtc_logo_image}, + {30, 2, 75, 60, &kkbtc_logo_image}, + {31, 2, 75, 60, &kkbtc_logo_image}, + {32, 2, 75, 60, &kkbtc_logo_image}, + {33, 2, 75, 60, &kkbtc_logo_image}, + {34, 2, 75, 60, &kkbtc_logo_image}, + {35, 2, 75, 60, &kkbtc_logo_image}, + {36, 2, 75, 60, &kkbtc_logo_image}, + {37, 2, 75, 60, &kkbtc_logo_image}, + {38, 2, 75, 60, &kkbtc_logo_image}, + {39, 2, 75, 60, &kkbtc_logo_image}, + {40, 2, 75, 60, &kkbtc_logo_image}, + {41, 2, 75, 60, &kkbtc_logo_image}, + {42, 2, 75, 60, &kkbtc_logo_image}, + {43, 2, 75, 60, &kkbtc_logo_image}, + {44, 2, 75, 60, &kkbtc_logo_image}, + {45, 2, 75, 60, &kkbtc_logo_image}, + {46, 2, 75, 60, &kkbtc_logo_image}, + {47, 2, 75, 60, &kkbtc_logo_image}, + {48, 2, 75, 60, &kkbtc_logo_image}, + {49, 2, 75, 60, &kkbtc_logo_image}, + {50, 2, 75, 60, &kkbtc_logo_image}, + {51, 2, 75, 60, &kkbtc_logo_image}, + {52, 2, 75, 60, &kkbtc_logo_image}, + {53, 2, 75, 60, &kkbtc_logo_image}, + {54, 2, 75, 60, &kkbtc_logo_image}, + {55, 2, 75, 60, &kkbtc_logo_image}, + {56, 2, 75, 60, &kkbtc_logo_image}, + {57, 2, 75, 60, &kkbtc_logo_image}, + {58, 2, 75, 60, &kkbtc_logo_image}, + {59, 2, 75, 60, &kkbtc_logo_image}, + {60, 2, 75, 60, &kkbtc_logo_image}, + {61, 2, 75, 60, &kkbtc_logo_image}, + {62, 2, 75, 60, &kkbtc_logo_image}, + {63, 2, 75, 60, &kkbtc_logo_image}, + {64, 2, 75, 60, &kkbtc_logo_image}, + {65, 2, 75, 60, &kkbtc_logo_image}, + {66, 2, 75, 60, &kkbtc_logo_image}, + {67, 2, 75, 60, &kkbtc_logo_image}, + {68, 2, 75, 60, &kkbtc_logo_image}, + {69, 2, 75, 60, &kkbtc_logo_image}, + {70, 2, 75, 60, &kkbtc_logo_image}, + {71, 2, 75, 60, &kkbtc_logo_image}, + {72, 2, 75, 60, &kkbtc_logo_image}, + {73, 2, 75, 60, &kkbtc_logo_image}, + {74, 2, 75, 60, &kkbtc_logo_image}, + {75, 2, 75, 60, &kkbtc_logo_image}, + {76, 2, 75, 60, &kkbtc_logo_image}, + {77, 2, 75, 60, &kkbtc_logo_image}, + {78, 2, 75, 60, &kkbtc_logo_image}, + {79, 2, 75, 60, &kkbtc_logo_image}, + {80, 2, 75, 60, &kkbtc_logo_image}, + {81, 2, 75, 60, &kkbtc_logo_image}, + {82, 2, 75, 60, &kkbtc_logo_image}, + {83, 2, 75, 60, &kkbtc_logo_image}, + {84, 2, 75, 60, &kkbtc_logo_image}, + {85, 2, 75, 60, &kkbtc_logo_image}, + {86, 2, 75, 60, &kkbtc_logo_image}, + {87, 2, 75, 60, &kkbtc_logo_image}, + {88, 2, 75, 60, &kkbtc_logo_image}, + {89, 2, 75, 60, &kkbtc_logo_image}, + {90, 2, 75, 60, &kkbtc_logo_image}, + {91, 2, 75, 60, &kkbtc_logo_image}, + {92, 2, 75, 60, &kkbtc_logo_image}, + {93, 2, 75, 60, &kkbtc_logo_image}, + {94, 2, 75, 60, &kkbtc_logo_image}, + } +}; + diff --git a/scripts/armEmulator/Dockerfile b/scripts/armEmulator/Dockerfile new file mode 100644 index 000000000..aa775b6b3 --- /dev/null +++ b/scripts/armEmulator/Dockerfile @@ -0,0 +1,20 @@ +FROM kkfirmware:v16 + +WORKDIR /kkemu +COPY ./ /kkemu + +ARG coinsupport="" +RUN cmake -C ./cmake/caches/emulator.cmake . \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_BUILD_TYPE=Debug \ + ${coinsupport} \ + -DCMAKE_COLOR_MAKEFILE=ON + +RUN make -j + +EXPOSE 11044/udp 11045/udp +EXPOSE 5000 +CMD ["/kkemu/scripts/armEmulator/run.sh"] + diff --git a/scripts/armEmulator/bridge.py b/scripts/armEmulator/bridge.py new file mode 100644 index 000000000..9d861a7b2 --- /dev/null +++ b/scripts/armEmulator/bridge.py @@ -0,0 +1,42 @@ + +from flask import Flask, Response, request, jsonify +app = Flask(__name__) + +import socket +import json +import binascii + + +PACKET_SIZE = 64 + +main = ('0.0.0.0', 11044) +debug = ('0.0.0.0', 11045) + +ms = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +ms.connect(main) + +ds = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +ds.connect(debug) + +app = Flask(__name__) + +@app.route('/exchange/', methods=['GET', 'POST']) +def exchange(kind): + + kk = ds if kind == 'debug' else ms + + if request.method == 'POST': + content = request.get_json(silent=True) + msg = bytearray.fromhex(content["data"]) + kk.send(msg) + return Response('{}', status=200, mimetype='application/json') + + if request.method == 'GET': + data = kk.recv(PACKET_SIZE) + body = '{"data":"' + binascii.hexlify(data).decode("utf-8") + '"}' + return Response(body, status=200, mimetype='application/json') + + return Response('{}', status=404, mimetype='application/json') + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') diff --git a/scripts/armEmulator/btcDockerStart.sh b/scripts/armEmulator/btcDockerStart.sh new file mode 100755 index 000000000..67e3f09e6 --- /dev/null +++ b/scripts/armEmulator/btcDockerStart.sh @@ -0,0 +1,2 @@ +docker-compose -f docker-compose-btc.yml up --build python-keepkey +docker kill armemulator_kkemu_1 \ No newline at end of file diff --git a/scripts/armEmulator/docker-compose-btc.yml b/scripts/armEmulator/docker-compose-btc.yml new file mode 100644 index 000000000..77eaaec39 --- /dev/null +++ b/scripts/armEmulator/docker-compose-btc.yml @@ -0,0 +1,42 @@ +version: '3' +services: + kkemu: + image: kktech/kkemu:latest_arm + build: + context: '../../' + args: + coinsupport: "-DCOIN_SUPPORT=BTC" + dockerfile: 'scripts/armEmulator/Dockerfile' + networks: + - local-net + ports: + - "127.0.0.1:11044:11044/udp" + - "127.0.0.1:11045:11045/udp" + - "127.0.0.1:5000:5000" + python-keepkey: + build: + context: '../../' + dockerfile: 'scripts/armEmulator/python-keepkey.Dockerfile' + volumes: + - test-reports:/kkemu/test-reports:rw + networks: + - local-net + depends_on: + - kkemu + firmware-unit: + build: + context: '../../' + dockerfile: 'scripts/armEmulator/Dockerfile' + volumes: + - test-reports:/kkemu/test-reports:rw + entrypoint: ['scripts/armEmulator/firmware-unit.sh'] + networks: + - local-net + depends_on: + - kkemu +networks: + local-net: + external: false +volumes: + test-reports: + external: false diff --git a/scripts/armEmulator/docker-compose.yml b/scripts/armEmulator/docker-compose.yml new file mode 100644 index 000000000..d1f54688c --- /dev/null +++ b/scripts/armEmulator/docker-compose.yml @@ -0,0 +1,40 @@ +version: '3' +services: + kkemu: + image: kktech/kkemu:latest_arm + build: + context: '../../' + dockerfile: 'scripts/armEmulator/Dockerfile' + networks: + - local-net + ports: + - "127.0.0.1:11044:11044/udp" + - "127.0.0.1:11045:11045/udp" + - "127.0.0.1:5000:5000" + python-keepkey: + build: + context: '../../' + dockerfile: 'scripts/armEmulator/python-keepkey.Dockerfile' + volumes: + - test-reports:/kkemu/test-reports:rw + networks: + - local-net + depends_on: + - kkemu + firmware-unit: + build: + context: '../../' + dockerfile: 'scripts/armEmulator/Dockerfile' + volumes: + - test-reports:/kkemu/test-reports:rw + entrypoint: ['scripts/armEmulator/firmware-unit.sh'] + networks: + - local-net + depends_on: + - kkemu +networks: + local-net: + external: false +volumes: + test-reports: + external: false diff --git a/scripts/armEmulator/dockerStart.sh b/scripts/armEmulator/dockerStart.sh new file mode 100755 index 000000000..b28700468 --- /dev/null +++ b/scripts/armEmulator/dockerStart.sh @@ -0,0 +1,2 @@ +docker-compose up --build python-keepkey +docker kill armemulator_kkemu_1 \ No newline at end of file diff --git a/scripts/armEmulator/firmware-unit.sh b/scripts/armEmulator/firmware-unit.sh new file mode 100755 index 000000000..c3c9374a6 --- /dev/null +++ b/scripts/armEmulator/firmware-unit.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +mkdir -p /kkemu/test-reports/firmware-unit +make xunit +echo "$?" > /kkemu/test-reports/firmware-unit/status +cp -r unittests/*.xml /kkemu/test-reports/firmware-unit diff --git a/scripts/armEmulator/python-keepkey-tests.sh b/scripts/armEmulator/python-keepkey-tests.sh new file mode 100755 index 000000000..ef4433f01 --- /dev/null +++ b/scripts/armEmulator/python-keepkey-tests.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +mkdir -p /kkemu/test-reports/python-keepkey +cd deps/python-keepkey/tests +KK_TRANSPORT_MAIN=kkemu:11044 KK_TRANSPORT_DEBUG=kkemu:11045 pytest -v --junitxml=/kkemu/test-reports/python-keepkey/junit.xml +echo "$?" > /kkemu/test-reports/python-keepkey/status diff --git a/scripts/armEmulator/python-keepkey.Dockerfile b/scripts/armEmulator/python-keepkey.Dockerfile new file mode 100644 index 000000000..8c76484d3 --- /dev/null +++ b/scripts/armEmulator/python-keepkey.Dockerfile @@ -0,0 +1,6 @@ +FROM kkfirmware:v16 + +WORKDIR /kkemu +COPY ./ /kkemu + +ENTRYPOINT ["/bin/sh", "./scripts/armEmulator/python-keepkey-tests.sh"] diff --git a/scripts/armEmulator/run.sh b/scripts/armEmulator/run.sh new file mode 100755 index 000000000..de61db8cd --- /dev/null +++ b/scripts/armEmulator/run.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -e + +python3 ./scripts/armEmulator/bridge.py & +./bin/kkemu diff --git a/scripts/build/docker/device/btcdebug.sh b/scripts/build/docker/device/btcdebug.sh new file mode 100755 index 000000000..e99b6b1dc --- /dev/null +++ b/scripts/build/docker/device/btcdebug.sh @@ -0,0 +1,25 @@ +#!/bin/bash -e + +KEEPKEY_FIRMWARE="$(dirname "$(dirname "$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")")")" +cd $KEEPKEY_FIRMWARE + +IMAGETAG=kkfirmware:v16 + +# docker image inspect $IMAGETAG > /dev/null || docker pull $IMAGETAG + +docker run -t \ + -v $(pwd):/root/keepkey-firmware:z \ + $IMAGETAG /bin/sh -c "\ + mkdir /root/build && cd /root/build && \ + cmake -C /root/keepkey-firmware/cmake/caches/device.cmake /root/keepkey-firmware \ + -DCOIN_SUPPORT=BTC \ + -DCMAKE_BUILD_TYPE=Debug \ + -DKK_DEBUG_LINK=ON \ + -DVARIANTS=NoObsoleteVariants \ + -DCMAKE_COLOR_MAKEFILE=ON &&\ + make && \ + mkdir -p /root/keepkey-firmware/bin && \ + cp -r /root/build /root/keepkey-firmware/bin/ && \ + cp bin/*.bin /root/keepkey-firmware/bin/ && \ + cp bin/*.elf /root/keepkey-firmware/bin/ && \ + chown -R \`stat -c \"%u:%g\" /root/keepkey-firmware\` /root/keepkey-firmware/bin" diff --git a/scripts/build/docker/device/btcrelease.sh b/scripts/build/docker/device/btcrelease.sh new file mode 100755 index 000000000..67ef4879e --- /dev/null +++ b/scripts/build/docker/device/btcrelease.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e + +KEEPKEY_FIRMWARE="$(dirname "$(dirname "$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")")")" +cd $KEEPKEY_FIRMWARE + +IMAGETAG=kkfirmware:v16 + +# docker image inspect $IMAGETAG > /dev/null || docker pull $IMAGETAG + +docker run -t \ + -v $(pwd):/root/keepkey-firmware:z \ + $IMAGETAG /bin/sh -c "\ + mkdir /root/build && cd /root/build && \ + cmake -C /root/keepkey-firmware/cmake/caches/device.cmake /root/keepkey-firmware \ + -DCOIN_SUPPORT=BTC \ + -DVARIANTS=NoObsoleteVariants \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DCMAKE_COLOR_MAKEFILE=ON &&\ + make && \ + mkdir -p /root/keepkey-firmware/bin && \ + cp -r /root/build /root/keepkey-firmware/bin/ && \ + cp bin/*.bin /root/keepkey-firmware/bin/ && \ + cp bin/*.elf /root/keepkey-firmware/bin/ && \ + chown -R \`stat -c \"%u:%g\" /root/keepkey-firmware\` /root/keepkey-firmware/bin" diff --git a/scripts/build/docker/device/debug.sh b/scripts/build/docker/device/debug.sh index 0150e9138..a56d513f7 100755 --- a/scripts/build/docker/device/debug.sh +++ b/scripts/build/docker/device/debug.sh @@ -3,9 +3,9 @@ KEEPKEY_FIRMWARE="$(dirname "$(dirname "$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")")")" cd $KEEPKEY_FIRMWARE -IMAGETAG=kktech/firmware:v15 +IMAGETAG=kkfirmware:v16 -docker image inspect $IMAGETAG > /dev/null || docker pull $IMAGETAG +# docker image inspect $IMAGETAG > /dev/null || docker pull $IMAGETAG docker run -t \ -v $(pwd):/root/keepkey-firmware:z \ @@ -14,6 +14,7 @@ docker run -t \ cmake -C /root/keepkey-firmware/cmake/caches/device.cmake /root/keepkey-firmware \ -DCMAKE_BUILD_TYPE=Debug \ -DKK_DEBUG_LINK=ON \ + -DVARIANTS=NoObsoleteVariants \ -DCMAKE_COLOR_MAKEFILE=ON &&\ make && \ mkdir -p /root/keepkey-firmware/bin && \ diff --git a/scripts/build/docker/device/devdebug.sh b/scripts/build/docker/device/devdebug.sh new file mode 100755 index 000000000..571c605bb --- /dev/null +++ b/scripts/build/docker/device/devdebug.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e + +KEEPKEY_FIRMWARE="$(dirname "$(dirname "$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")")")" +cd $KEEPKEY_FIRMWARE + +IMAGETAG=kkfirmware:v16 + +docker run -t \ + -v $(pwd):/root/keepkey-firmware:z \ + $IMAGETAG /bin/sh -c "\ + mkdir /root/build && cd /root/build && \ + cmake -C /root/keepkey-firmware/cmake/caches/devdevice.cmake /root/keepkey-firmware \ + -DCMAKE_BUILD_TYPE=Debug \ + -DKK_DEBUG_LINK=ON \ + -DVARIANTS=NoObsoleteVariants \ + -DDEVDEBUG=true \ + -DTWODISP=true \ + -DCMAKE_COLOR_MAKEFILE=ON &&\ + make && \ + mkdir -p /root/keepkey-firmware/bin && \ + cp -r /root/build /root/keepkey-firmware/bin/ && \ + cp bin/*.bin /root/keepkey-firmware/bin/ && \ + cp bin/*.elf /root/keepkey-firmware/bin/ && \ + chown -R \`stat -c \"%u:%g\" /root/keepkey-firmware\` /root/keepkey-firmware/bin" diff --git a/scripts/build/docker/device/release.sh b/scripts/build/docker/device/release.sh index 18cfe53dc..15baacc9c 100755 --- a/scripts/build/docker/device/release.sh +++ b/scripts/build/docker/device/release.sh @@ -3,15 +3,16 @@ KEEPKEY_FIRMWARE="$(dirname "$(dirname "$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")")")" cd $KEEPKEY_FIRMWARE -IMAGETAG=kktech/firmware:v15 +IMAGETAG=kkfirmware:v16 -docker image inspect $IMAGETAG > /dev/null || docker pull $IMAGETAG +# docker image inspect $IMAGETAG > /dev/null || docker pull $IMAGETAG docker run -t \ -v $(pwd):/root/keepkey-firmware:z \ $IMAGETAG /bin/sh -c "\ mkdir /root/build && cd /root/build && \ cmake -C /root/keepkey-firmware/cmake/caches/device.cmake /root/keepkey-firmware \ + -DVARIANTS=NoObsoleteVariants \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_COLOR_MAKEFILE=ON &&\ make && \ diff --git a/scripts/build/docker/emulator/btcdebug.sh b/scripts/build/docker/emulator/btcdebug.sh new file mode 100755 index 000000000..c6fcc0d0a --- /dev/null +++ b/scripts/build/docker/emulator/btcdebug.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e + +KEEPKEY_FIRMWARE="$(dirname "$(dirname "$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")")")" +cd $KEEPKEY_FIRMWARE + +IMAGETAG=kkfirmware:v16 + +# docker pull $IMAGETAG + +docker run -t \ + -v $(pwd):/root/keepkey-firmware:z \ + $IMAGETAG /bin/sh -c "\ + rm -rf /root/keepkey-firmware/build && \ + mkdir /root/build && cd /root/build && \ + cmake -C /root/keepkey-firmware/cmake/caches/emulator.cmake /root/keepkey-firmware \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCOIN_SUPPORT=BTC \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_COLOR_MAKEFILE=ON &&\ + make all && \ + (make xunit || true) && \ + cp -r /root/build /root/keepkey-firmware/build && \ + chown -R \`stat -c \"%u:%g\" /root/keepkey-firmware\` /root/keepkey-firmware/build" diff --git a/scripts/build/docker/emulator/debug.sh b/scripts/build/docker/emulator/debug.sh index 9fec8264d..766646e7d 100755 --- a/scripts/build/docker/emulator/debug.sh +++ b/scripts/build/docker/emulator/debug.sh @@ -3,9 +3,9 @@ KEEPKEY_FIRMWARE="$(dirname "$(dirname "$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")")")" cd $KEEPKEY_FIRMWARE -IMAGETAG=kktech/firmware:v15 +IMAGETAG=kkfirmware:v16 -docker pull $IMAGETAG +# docker pull $IMAGETAG docker run -t \ -v $(pwd):/root/keepkey-firmware:z \ diff --git a/scripts/dev2/debug.sh b/scripts/dev2/debug.sh new file mode 100755 index 000000000..e665a7816 --- /dev/null +++ b/scripts/dev2/debug.sh @@ -0,0 +1,7 @@ +#! /bin/bash -e + +KEEPKEY_FIRMWARE="$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")" +CONFIG_SCRIPTS="/usr/local/share/openocd/scripts/" + +openocd -s "$CONFIG_SCRIPTS" -f "$KEEPKEY_FIRMWARE/dev2/openocd/stm32f4x.cfg" -c "init" -c "halt" -c "reset halt" + diff --git a/scripts/dev2/load-bootloader.sh b/scripts/dev2/load-bootloader.sh new file mode 100755 index 000000000..7c2b78fab --- /dev/null +++ b/scripts/dev2/load-bootloader.sh @@ -0,0 +1,13 @@ +#! /bin/bash + +KEEPKEY_FIRMWARE="$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")" +CONFIG_SCRIPTS="/usr/local/share/openocd/scripts/" + +BIN_FILE=$KEEPKEY_FIRMWARE/bin/bootloader.bin + +openocd -s "$CONFIG_SCRIPTS" -f "$KEEPKEY_FIRMWARE/scripts/dev2/openocd/stm32f4x.cfg" -c "program $BIN_FILE 0x08020000 verify exit" +if [[ $? -ne 0 ]]; then + echo $? + echo "error in loading bootloader" + exit +fi diff --git a/scripts/dev2/load-bootstrap.sh b/scripts/dev2/load-bootstrap.sh new file mode 100755 index 000000000..b40c2371c --- /dev/null +++ b/scripts/dev2/load-bootstrap.sh @@ -0,0 +1,12 @@ +#! /bin/bash + +KEEPKEY_FIRMWARE="$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")" +CONFIG_SCRIPTS="/usr/local/share/openocd/scripts/" + +BIN_FILE=$KEEPKEY_FIRMWARE/bin/bootstrap.bin +openocd -s "$CONFIG_SCRIPTS" -f "$KEEPKEY_FIRMWARE/scripts/dev2/openocd/stm32f4x.cfg" -c "program $BIN_FILE 0x08000000 verify exit" +if [[ $? -ne 0 ]]; then + echo $? + echo "error in loading bootstrap" + exit +fi diff --git a/scripts/dev2/load-firmare.sh b/scripts/dev2/load-firmare.sh new file mode 100755 index 000000000..4581c3367 --- /dev/null +++ b/scripts/dev2/load-firmare.sh @@ -0,0 +1,12 @@ +#! /bin/bash + +KEEPKEY_FIRMWARE="$(dirname "$(dirname "$( cd "$(dirname "$0")" ; pwd -P )")")" +CONFIG_SCRIPTS="/usr/local/share/openocd/scripts/" + +BIN_FILE=$KEEPKEY_FIRMWARE/bin/firmware.keepkey.elf +openocd -s "$CONFIG_SCRIPTS" -f "$KEEPKEY_FIRMWARE/scripts/dev2/openocd/stm32f4x.cfg" -c "init" -c "reset init" -c "program $BIN_FILE verify reset 0x08080000" +if [[ $? -ne 0 ]]; then + echo $? + echo "error in loading application image" + exit +fi diff --git a/scripts/dev2/openocd/stm32f4x.cfg b/scripts/dev2/openocd/stm32f4x.cfg new file mode 100644 index 000000000..ad63c92ae --- /dev/null +++ b/scripts/dev2/openocd/stm32f4x.cfg @@ -0,0 +1,154 @@ +# script for stm32f4x family + +# +# stm32 devices support both JTAG and SWD transports. +# + +adapter driver jlink +set using_jtag 0 +set using_hla 0 + +transport select swd + +source [find target/swj-dp.tcl] +source [find mem_helper.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME stm32f4x +} + +set _ENDIAN little + +# Work-area is a space in RAM used for flash programming +# By default use 32kB (Available RAM in smallest device STM32F410) +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x10000 +} + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + if { [using_jtag] } { + # See STM Document RM0090 + # Section 38.6.3 - corresponds to Cortex-M4 r0p1 + set _CPUTAPID 0x4ba00477 + } { + set _CPUTAPID 0x2ba01477 + } +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +if {[using_jtag]} { + jtag newtap $_CHIPNAME bs -irlen 5 +} + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME stm32f2x 0 0 0 0 $_TARGETNAME + +flash bank $_CHIPNAME.otp stm32f2x 0x1fff7800 0 0 0 $_TARGETNAME + +if { [info exists QUADSPI] && $QUADSPI } { + set a [llength [flash list]] + set _QSPINAME $_CHIPNAME.qspi + flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_TARGETNAME 0xA0001000 +} + +# JTAG speed should be <= F_CPU/6. F_CPU after reset is 16MHz, so use F_JTAG = 2MHz +# +# Since we may be running of an RC oscilator, we crank down the speed a +# bit more to be on the safe side. Perhaps superstition, but if are +# running off a crystal, we can run closer to the limit. Note +# that there can be a pretty wide band where things are more or less stable. +adapter speed 2000 + +adapter srst delay 100 +if {[using_jtag]} { + jtag_ntrst_delay 100 +} + +reset_config srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +$_TARGETNAME configure -event examine-end { + # Enable debug during low power modes (uses more power) + # DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP + mmw 0xE0042004 0x00000007 0 + + # Stop watchdog counters during halt + # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP + mmw 0xE0042008 0x00001800 0 +} + +tpiu create $_CHIPNAME.tpiu -dap $_CHIPNAME.dap -ap-num 0 -baseaddr 0xE0040000 + +lappend _telnet_autocomplete_skip _proc_pre_enable_$_CHIPNAME.tpiu +proc _proc_pre_enable_$_CHIPNAME.tpiu {_chipname} { + targets $_chipname.cpu + + if { [$_chipname.tpiu cget -protocol] eq "sync" } { + switch [$_chipname.tpiu cget -port-width] { + 1 { + # Set TRACE_IOEN; TRACE_MODE to sync 1 bit; GPIOE[2-3] to AF0 + mmw 0xE0042004 0x00000060 0x000000c0 + mmw 0x40021020 0x00000000 0x0000ff00 + mmw 0x40021000 0x000000a0 0x000000f0 + mmw 0x40021008 0x000000f0 0x00000000 + } + 2 { + # Set TRACE_IOEN; TRACE_MODE to sync 2 bit; GPIOE[2-4] to AF0 + mmw 0xE0042004 0x000000a0 0x000000c0 + mmw 0x40021020 0x00000000 0x000fff00 + mmw 0x40021000 0x000002a0 0x000003f0 + mmw 0x40021008 0x000003f0 0x00000000 + } + 4 { + # Set TRACE_IOEN; TRACE_MODE to sync 4 bit; GPIOE[2-6] to AF0 + mmw 0xE0042004 0x000000e0 0x000000c0 + mmw 0x40021020 0x00000000 0x0fffff00 + mmw 0x40021000 0x00002aa0 0x00003ff0 + mmw 0x40021008 0x00003ff0 0x00000000 + } + } + } else { + # Set TRACE_IOEN; TRACE_MODE to async + mmw 0xE0042004 0x00000020 0x000000c0 + } +} + +$_CHIPNAME.tpiu configure -event pre-enable "_proc_pre_enable_$_CHIPNAME.tpiu $_CHIPNAME" + +$_TARGETNAME configure -event reset-init { + # Configure PLL to boost clock to HSI x 4 (64 MHz) + mww 0x40023804 0x08012008 ;# RCC_PLLCFGR 16 Mhz /8 (M) * 128 (N) /4(P) + mww 0x40023C00 0x00000102 ;# FLASH_ACR = PRFTBE | 2(Latency) + mmw 0x40023800 0x01000000 0 ;# RCC_CR |= PLLON + sleep 10 ;# Wait for PLL to lock + mmw 0x40023808 0x00001000 0 ;# RCC_CFGR |= RCC_CFGR_PPRE1_DIV2 + mmw 0x40023808 0x00000002 0 ;# RCC_CFGR |= RCC_CFGR_SW_PLL + + # Boost JTAG frequency + adapter speed 8000 +} + +$_TARGETNAME configure -event reset-start { + # Reduce speed since CPU speed will slow down to 16MHz with the reset + adapter speed 2000 +} + diff --git a/scripts/emulator/Dockerfile b/scripts/emulator/Dockerfile index e0b2c6d3f..e90cb6420 100644 --- a/scripts/emulator/Dockerfile +++ b/scripts/emulator/Dockerfile @@ -1,13 +1,15 @@ -FROM kktech/firmware:v15 +FROM kktech/firmware:v16 WORKDIR /kkemu COPY ./ /kkemu +ARG coinsupport="" RUN cmake -C ./cmake/caches/emulator.cmake . \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_BUILD_TYPE=Debug \ + ${coinsupport} \ -DCMAKE_COLOR_MAKEFILE=ON RUN make -j diff --git a/scripts/emulator/btcDockerStart.sh b/scripts/emulator/btcDockerStart.sh new file mode 100755 index 000000000..85d2b8a9b --- /dev/null +++ b/scripts/emulator/btcDockerStart.sh @@ -0,0 +1,2 @@ +docker-compose -f docker-compose-btc.yml up --build python-keepkey +docker kill emulator_kkemu_1 \ No newline at end of file diff --git a/scripts/emulator/docker-compose-btc.yml b/scripts/emulator/docker-compose-btc.yml new file mode 100644 index 000000000..1b175f3aa --- /dev/null +++ b/scripts/emulator/docker-compose-btc.yml @@ -0,0 +1,42 @@ +version: '3' +services: + kkemu: + image: kktech/kkemu:latest + build: + context: '../../' + args: + coinsupport: "-DCOIN_SUPPORT=BTC" + dockerfile: 'scripts/emulator/Dockerfile' + networks: + - local-net + ports: + - "127.0.0.1:11044:11044/udp" + - "127.0.0.1:11045:11045/udp" + - "127.0.0.1:5000:5000" + python-keepkey: + build: + context: '../../' + dockerfile: 'scripts/emulator/python-keepkey.Dockerfile' + volumes: + - test-reports:/kkemu/test-reports:rw + networks: + - local-net + depends_on: + - kkemu + firmware-unit: + build: + context: '../../' + dockerfile: 'scripts/emulator/Dockerfile' + volumes: + - test-reports:/kkemu/test-reports:rw + entrypoint: ['scripts/emulator/firmware-unit.sh'] + networks: + - local-net + depends_on: + - kkemu +networks: + local-net: + external: false +volumes: + test-reports: + external: false diff --git a/scripts/emulator/dockerStart.sh b/scripts/emulator/dockerStart.sh new file mode 100755 index 000000000..d431bb0b1 --- /dev/null +++ b/scripts/emulator/dockerStart.sh @@ -0,0 +1,2 @@ +docker-compose up --build python-keepkey +docker kill emulator_kkemu_1 \ No newline at end of file diff --git a/scripts/emulator/python-keepkey.Dockerfile b/scripts/emulator/python-keepkey.Dockerfile index 26f358a0b..8c340edc4 100644 --- a/scripts/emulator/python-keepkey.Dockerfile +++ b/scripts/emulator/python-keepkey.Dockerfile @@ -1,4 +1,4 @@ -FROM kktech/firmware:v15 +FROM kktech/firmware:v16 WORKDIR /kkemu COPY ./ /kkemu diff --git a/scripts/debug.sh b/scripts/legacy-jtag/debug.sh similarity index 100% rename from scripts/debug.sh rename to scripts/legacy-jtag/debug.sh diff --git a/scripts/load-all.sh b/scripts/legacy-jtag/load-all.sh similarity index 100% rename from scripts/load-all.sh rename to scripts/legacy-jtag/load-all.sh diff --git a/scripts/load-blup.sh b/scripts/legacy-jtag/load-blup.sh similarity index 100% rename from scripts/load-blup.sh rename to scripts/legacy-jtag/load-blup.sh diff --git a/scripts/load-bootloader.sh b/scripts/legacy-jtag/load-bootloader.sh similarity index 100% rename from scripts/load-bootloader.sh rename to scripts/legacy-jtag/load-bootloader.sh diff --git a/scripts/load-bootstrap.sh b/scripts/legacy-jtag/load-bootstrap.sh similarity index 100% rename from scripts/load-bootstrap.sh rename to scripts/legacy-jtag/load-bootstrap.sh diff --git a/scripts/load-bs.sh b/scripts/legacy-jtag/load-bs.sh similarity index 100% rename from scripts/load-bs.sh rename to scripts/legacy-jtag/load-bs.sh diff --git a/scripts/load-firmare.sh b/scripts/legacy-jtag/load-firmare.sh similarity index 100% rename from scripts/load-firmare.sh rename to scripts/legacy-jtag/load-firmare.sh diff --git a/scripts/load-fw.sh b/scripts/legacy-jtag/load-fw.sh similarity index 100% rename from scripts/load-fw.sh rename to scripts/legacy-jtag/load-fw.sh diff --git a/scripts/load-variant.sh b/scripts/legacy-jtag/load-variant.sh similarity index 100% rename from scripts/load-variant.sh rename to scripts/legacy-jtag/load-variant.sh diff --git a/scripts/load.sh b/scripts/legacy-jtag/load.sh similarity index 100% rename from scripts/load.sh rename to scripts/legacy-jtag/load.sh diff --git a/scripts/openocd/jlink.cfg b/scripts/legacy-jtag/openocd/jlink.cfg similarity index 100% rename from scripts/openocd/jlink.cfg rename to scripts/legacy-jtag/openocd/jlink.cfg diff --git a/scripts/openocd/openocd.cfg b/scripts/legacy-jtag/openocd/openocd.cfg similarity index 100% rename from scripts/openocd/openocd.cfg rename to scripts/legacy-jtag/openocd/openocd.cfg diff --git a/scripts/openocd/stm32f2x.cfg b/scripts/legacy-jtag/openocd/stm32f2x.cfg similarity index 100% rename from scripts/openocd/stm32f2x.cfg rename to scripts/legacy-jtag/openocd/stm32f2x.cfg diff --git a/scripts/readme.txt b/scripts/readme.txt new file mode 100644 index 000000000..8d68a5d88 --- /dev/null +++ b/scripts/readme.txt @@ -0,0 +1,30 @@ +Description of the shell files in the /scripts directory tree for building keepkey firmware + +scripts/emulator +These files run the emulator for python testing without a keepkey device on an amd64 platform. + +scripts/armEmulator +These files are the equivalent to the /scripts/emulator files but they run on an arm64 platform + +scripts/build/docker/device +initiate these scripts from top level directory keepkey-firmware/ +btcdebug.sh: build debug version of bitcoin-only firmware +btcrelease.sh: build release version of bitcoin-only firmware (must be signed) +debug.sh: build debug version of full-featured firmware +release.sh: build release version of full-featured firmware (must be signed) +devdebug.sh: build debug version of firmware for development board (described elsewhere) + +script/build/docker/emulator +These files kick off building and running the emulator on debug firmware. They can be used +stand-alone to test code coverage directly without going through the comm interface as well +as interface testing though the scripts/emulator interface test suite. + +scripts/dev2 +These files are used with the development board for jtag debugging (described in the dev2 readme) + +scripts/legacy-jtag +These are the legacy script files for keepkey boards. They use openocd to load firmware builds +onto a keepkey board and debug via jtag. These files are only useful on special board +builds that do not have the jtag port locked. All out-of-the-box Keepkey devices have permanently +locked jtag ports as a security feature. + diff --git a/tools/blupdater/CMakeLists.txt b/tools/blupdater/CMakeLists.txt index 990529f70..ba8223849 100644 --- a/tools/blupdater/CMakeLists.txt +++ b/tools/blupdater/CMakeLists.txt @@ -9,22 +9,13 @@ if(NOT ${KK_EMULATOR}) include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) set(linker_script ${CMAKE_CURRENT_SOURCE_DIR}/blupdater.ld) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T${linker_script} -L${CMAKE_BINARY_DIR}/lib") - set(LINK_FLAGS - kkfirmware - kkboard - kktransport - trezorcrypto - -lopencm3_stm32f2 - -lc - -lm) - add_library(payload STATIC payload.o) @@ -46,14 +37,21 @@ if(NOT ${KK_EMULATOR}) LINKER_LANGUAGE C) add_executable(blupdater.elf ${sources}) + + if("${DEVDEBUG}" STREQUAL "true") + set(targtype -lopencm3_stm32f4) + else() + set(targtype -lopencm3_stm32f2) + endif() + target_link_libraries(blupdater.elf payload kkboard kkboard.keepkey kkvariant.keepkey - trezorcrypto + hwcrypto kkrand - -lopencm3_stm32f2 + ${targtype} -lc -lm) add_custom_command(TARGET blupdater.elf diff --git a/tools/blupdater/main.c b/tools/blupdater/main.c index 14b48efd0..2af1be45c 100644 --- a/tools/blupdater/main.c +++ b/tools/blupdater/main.c @@ -51,7 +51,7 @@ #include "keepkey/board/keepkey_flash.h" #include "keepkey/board/draw.h" #include "keepkey/board/layout.h" -#include "trezor/crypto/sha2.h" +#include "hwcrypto/crypto/sha2.h" #include "keepkey/firmware/app_layout.h" diff --git a/tools/bootloader/CMakeLists.txt b/tools/bootloader/CMakeLists.txt index 60b2c46a8..b002c8e81 100644 --- a/tools/bootloader/CMakeLists.txt +++ b/tools/bootloader/CMakeLists.txt @@ -1,5 +1,13 @@ if(NOT ${KK_EMULATOR}) + if("${DEVDEBUG}" STREQUAL "true") + set(LIBCM3TARG opencm3_stm32f4) + set(ASMDEF "-Xassembler --defsym -Xassembler DEV_DEBUG") + else() + set(LIBCM3TARG opencm3_stm32f2) + set(ASMDEF "") + endif() + set(sources main.c usb_flash.c @@ -10,25 +18,27 @@ if(NOT ${KK_EMULATOR}) include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) set(linker_script ${CMAKE_CURRENT_SOURCE_DIR}/bootloader.ld) set(CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} -T${linker_script} -L${CMAKE_BINARY_DIR}/lib") - + "${CMAKE_EXE_LINKER_FLAGS} ${ASMDEF} -T${linker_script} -L${CMAKE_BINARY_DIR}/lib") + add_executable(bootloader.elf ${sources}) + target_link_libraries(bootloader.elf - kkboard - kkboard.keepkey - kkvariant.keepkey - kkvariant.poweredBy - kktransport - trezorcrypto - kkrand - -lopencm3_stm32f2 - -lc - -lm) + kkboard + kkboard.keepkey + kkvariant.keepkey + kkvariant.poweredBy + kktransport + hwcrypto + kkrand + -l${LIBCM3TARG} + -lc + -lm) + add_custom_command(TARGET bootloader.elf POST_BUILD COMMAND ${CMAKE_OBJCOPY} ARGS -O binary diff --git a/tools/bootloader/bl_mpu.c b/tools/bootloader/bl_mpu.c index 7895ff897..33aed9eea 100644 --- a/tools/bootloader/bl_mpu.c +++ b/tools/bootloader/bl_mpu.c @@ -22,7 +22,11 @@ #ifndef EMULATOR #include #include +#ifdef DEV_DEBUG +#include +#else #include +#endif #include #endif diff --git a/tools/bootloader/isr.s b/tools/bootloader/isr.s index 24234febc..86cee1fef 100644 --- a/tools/bootloader/isr.s +++ b/tools/bootloader/isr.s @@ -110,7 +110,7 @@ b_continue: // disable request - mvns r0, #128 // BUTTON_EXTI - 0X80 (button) + mvns r0, #button_mask ldr r2, =exti_imr ldr r3, [r2, #0] // get what is in exti_imr ands r3, r0 // clear the bit @@ -123,7 +123,7 @@ b_continue: str r0, [r3, #0] // write back to IMR // reset button request, bit 7 in 0x40013c14 - movs r0, #128 // BUTTON_EXTI - 0X80 + movs r0, #button_mask ldr r1, =exti_pr str r0, [r1, #0] @@ -143,7 +143,7 @@ b_continue: svhandler_button_usr_return: // re-enable button request - movs r0, #128 // BUTTON_EXTI - 0X80 + movs r0, #button_mask ldr r2, =exti_imr ldr r3, [r2, #0] orrs r3, r0 // set the bit @@ -183,8 +183,12 @@ svhandler_button_usr_return: .set exti_emr, 0x40013c04 .set exti_pr, 0x40013c14 - - + .if DEV_DEBUG == 1 + .set button_mask, 512 // BUTTON_EXTI - 0x200 + .else + .set button_mask, 128 // BUTTON_EXTI - 0X80 + .endif + .global tim4_isr .type tim4_isr STT_FUNC diff --git a/tools/bootloader/main.c b/tools/bootloader/main.c index 0982af3ad..ffcc8cba1 100644 --- a/tools/bootloader/main.c +++ b/tools/bootloader/main.c @@ -23,7 +23,11 @@ #include #include #include +#ifdef DEV_DEBUG +#include +#else #include +#endif #include #include #include @@ -51,8 +55,16 @@ #include "keepkey/rand/rng.h" #include "keepkey/variant/keepkey.h" #include "keepkey/variant/poweredBy.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/rand.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/rand.h" + +#ifdef DEV_DEBUG +#include "keepkey/board/pin.h" +#endif + +#ifdef TWO_DISP +#include "keepkey/board/ssd1351/ssd1351.h" +#endif #include #include @@ -163,6 +175,8 @@ void capture_reset_param(void) { _param_3 = 0; } +void display_configure_io(void); + /// Bootloader Board Initialization static void bootloader_init(void) { cm_enable_interrupts(); @@ -174,6 +188,9 @@ static void bootloader_init(void) { usart_init(); keepkey_leds_init(); storage_sectorInit(); +#ifdef DEV_DEBUG + SSD1351_CSInit(); +#endif display_hw_init(); layout_init(display_canvas_init()); capture_reset_param(); @@ -181,8 +198,12 @@ static void bootloader_init(void) { /// Enable the timer interrupts static void clock_init(void) { +#ifdef DEV_DEBUG + rcc_clock_setup_pll(&rcc_hse_12mhz_3v3[RCC_CLOCK_3V3_168MHZ]); +#else struct rcc_clock_scale clock = rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_120MHZ]; rcc_clock_setup_hse_3v3(&clock); +#endif rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_GPIOB); @@ -265,7 +286,7 @@ static void boot(void) { } memzero(flashed_firmware_hash, sizeof(flashed_firmware_hash)); memory_firmware_hash(flashed_firmware_hash); - char hash_str[2 * 32 + 1]; + static char hash_str[2 * 32 + 1]; data2hex(flashed_firmware_hash, 32, hash_str); kk_strlwr(hash_str); if (!confirm_without_button_request( @@ -315,6 +336,12 @@ int main(void) { layout_simple_message("booting..."); +#ifdef TWO_DISP + SSD1351_Init(); + SSD1351_FillScreen(SSD1351_BLACK); + SSD1351_WriteString(0, 0, "bootloader", Font_7x10, SSD1351_BLUE, SSD1351_BLACK); +#endif + #if !defined(DEBUG_ON) && (MEMORY_PROTECT == 0) #error "To compile release version, please set MEMORY_PROTECT flag" #elif !defined(DEBUG_ON) diff --git a/tools/bootloader/usb_flash.c b/tools/bootloader/usb_flash.c index d1f26e393..6cd4f12c0 100644 --- a/tools/bootloader/usb_flash.c +++ b/tools/bootloader/usb_flash.c @@ -29,8 +29,8 @@ #include "keepkey/board/usb.h" #include "keepkey/board/signatures.h" #include "keepkey/bootloader/usb_flash.h" -#include "trezor/crypto/sha2.h" -#include "trezor/crypto/memzero.h" +#include "hwcrypto/crypto/sha2.h" +#include "hwcrypto/crypto/memzero.h" #include "keepkey/transport/interface.h" #include "keepkey/board/supervise.h" diff --git a/tools/bootstrap/CMakeLists.txt b/tools/bootstrap/CMakeLists.txt index 9b2853b12..2d54481ae 100644 --- a/tools/bootstrap/CMakeLists.txt +++ b/tools/bootstrap/CMakeLists.txt @@ -1,5 +1,4 @@ if(NOT ${KK_EMULATOR}) - set(sources main.c startup.s) @@ -7,16 +6,28 @@ if(NOT ${KK_EMULATOR}) include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) set(linker_script ${CMAKE_CURRENT_SOURCE_DIR}/bootstrap.ld) + if(CMAKE_C_COMPILER_VERSION VERSION_LESS "12.2.1") + set(warnopt "") + else() + set(warnopt "-Wl,--no-warn-rwx-segments") + endif() + set(CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} -T${linker_script} -L${CMAKE_BINARY_DIR}/lib") + "${CMAKE_EXE_LINKER_FLAGS} ${warnopt} -T${linker_script} -L${CMAKE_BINARY_DIR}/lib") + if("${DEVDEBUG}" STREQUAL "true") + set(targtype -lopencm3_stm32f4) + else() + set(targtype -lopencm3_stm32f2) + endif() + add_executable(bootstrap.elf ${sources}) target_link_libraries(bootstrap.elf - -lopencm3_stm32f2 + ${targtype} -lc -lm) add_custom_command(TARGET bootstrap.elf diff --git a/tools/display_test/CMakeLists.txt b/tools/display_test/CMakeLists.txt index 0d280ab80..1606e7429 100644 --- a/tools/display_test/CMakeLists.txt +++ b/tools/display_test/CMakeLists.txt @@ -1,3 +1,7 @@ +if("${VARIANTS}" STREQUAL "NoObsoleteVariants") + return() +endif() + if(NOT ${KK_EMULATOR}) set(sources main.c @@ -6,22 +10,19 @@ if(NOT ${KK_EMULATOR}) include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) set(linker_script ${CMAKE_CURRENT_SOURCE_DIR}/display_test.ld) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T${linker_script} -L${CMAKE_BINARY_DIR}/lib") - set(LINK_FLAGS - kkfirmware - kkboard - kktransport - trezorcrypto - -lopencm3_stm32f2 - -lc - -lm) - + if("${DEVDEBUG}" STREQUAL "true") + set(targtype -lopencm3_stm32f4) + else() + set(targtype -lopencm3_stm32f2) + endif() + add_executable(display_test.elf ${sources}) target_link_libraries(display_test.elf kkfirmware @@ -30,9 +31,9 @@ if(NOT ${KK_EMULATOR}) kkboard.keepkey kkvariant.keepkey kktransport - trezorcrypto + hwcrypto kkrand - -lopencm3_stm32f2 + ${targtype} -lc -lm) add_custom_command(TARGET display_test.elf diff --git a/tools/emulator/CMakeLists.txt b/tools/emulator/CMakeLists.txt index 7b0574e65..9f0d3acd3 100644 --- a/tools/emulator/CMakeLists.txt +++ b/tools/emulator/CMakeLists.txt @@ -6,7 +6,7 @@ if(${KK_EMULATOR}) include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) add_executable(kkemu ${sources}) @@ -18,7 +18,7 @@ if(${KK_EMULATOR}) kkvariant.keepkey kkvariant.salt kktransport - trezorcrypto + hwcrypto qrcodegenerator SecAESSTM32 kkrand diff --git a/tools/emulator/main.cpp b/tools/emulator/main.cpp index dbe25518d..e8ef67cd2 100644 --- a/tools/emulator/main.cpp +++ b/tools/emulator/main.cpp @@ -37,6 +37,7 @@ extern "C" { #include #include #include +#include #define APP_VERSIONS \ "VERSION" VERSION_STR(MAJOR_VERSION) "." VERSION_STR( \ diff --git a/tools/firmware/CMakeLists.txt b/tools/firmware/CMakeLists.txt index 18c50e411..e4a851a50 100644 --- a/tools/firmware/CMakeLists.txt +++ b/tools/firmware/CMakeLists.txt @@ -1,4 +1,12 @@ if(NOT ${KK_EMULATOR}) + + set(TARGESTR "firmware.keepkey.elf") + set(TARGBSTR "firmware.keepkey.bin") + if("${COIN_SUPPORT}" STREQUAL "BTC") + set(TARGESTR "firmware.keepkeybtc.elf") + set(TARGBSTR "firmware.keepkeybtc.bin") + endif() + set(sources keepkey_main.c startup.s @@ -9,42 +17,68 @@ if(NOT ${KK_EMULATOR}) include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) set(linker_script ${CMAKE_CURRENT_SOURCE_DIR}/keepkey.ld) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T${linker_script} -L${CMAKE_BINARY_DIR}/lib") - set(LINK_FLAGS - kkfirmware - kkboard - kktransport - trezorcrypto - -lopencm3_stm32f2 - -lc - -lm) + if("${DEVDEBUG}" STREQUAL "true") + set(targtype -lopencm3_stm32f4) + else() + set(targtype -lopencm3_stm32f2) + endif() + + add_executable(${TARGESTR} ${sources}) + + set(libraries + kkfirmware + kkfirmware.keepkey + kkboard + kkboard.keepkey + kkvariant.keepkey + kktransport + hwcrypto + qrcodegenerator + SecAESSTM32 + kkrand) - add_executable(firmware.keepkey.elf ${sources}) - target_link_libraries(firmware.keepkey.elf - kkfirmware - kkfirmware.keepkey - kkboard - kkboard.keepkey - kkvariant.keepkey + if("${COIN_SUPPORT}" STREQUAL "BTC") + else() + list(APPEND libraries kkvariant.salt - kktransport - trezorcrypto - qrcodegenerator - SecAESSTM32 - kkrand - -lopencm3_stm32f2 + ) + endif() + +# these nanopb files need to be removed and since this is the last binary built, they +# are set to be removed post-build. See lib/CMakeLists.txt for further explanation. + set(nanopb_rmsources + ${CMAKE_SOURCE_DIR}/lib/transport/pb_common.c + ${CMAKE_SOURCE_DIR}/lib/transport/pb_decode.c + ${CMAKE_SOURCE_DIR}/lib/transport/pb_encode.c) + + set(nanopb_rminc + ${CMAKE_SOURCE_DIR}/include/pb_common.h + ${CMAKE_SOURCE_DIR}/include/pb_decode.h + ${CMAKE_SOURCE_DIR}/include/pb_encode.h + ${CMAKE_SOURCE_DIR}/include/pb.h) + + target_link_libraries(${TARGESTR} + ${libraries} + ${targtype} -lc -lm) - add_custom_command(TARGET firmware.keepkey.elf - POST_BUILD - COMMAND ${CMAKE_OBJCOPY} ARGS -O binary - ${CMAKE_BINARY_DIR}/bin/firmware.keepkey.elf - ${CMAKE_BINARY_DIR}/bin/firmware.keepkey.bin) -endif() + add_custom_command(TARGET ${TARGESTR} + POST_BUILD + COMMAND ${CMAKE_OBJCOPY} ARGS -O binary + ${CMAKE_BINARY_DIR}/bin/${TARGESTR} + ${CMAKE_BINARY_DIR}/bin/${TARGBSTR} + COMMAND + ${CMAKE_COMMAND} -E rm ${nanopb_rmsources} + COMMAND + ${CMAKE_COMMAND} -E rm ${nanopb_rminc} + COMMAND_EXPAND_LISTS) + +endif() # KK_EMULATOR \ No newline at end of file diff --git a/tools/firmware/keepkey_main.c b/tools/firmware/keepkey_main.c index fcf4aa9cb..9a4a4d007 100644 --- a/tools/firmware/keepkey_main.c +++ b/tools/firmware/keepkey_main.c @@ -43,7 +43,11 @@ #include "keepkey/firmware/home_sm.h" #include "keepkey/firmware/storage.h" #include "keepkey/rand/rng.h" -#include "trezor/crypto/rand.h" +#include "hwcrypto/crypto/rand.h" + +#ifdef TWO_DISP +#include "keepkey/board/ssd1351/ssd1351.h" +#endif #include #include @@ -184,6 +188,10 @@ int main(void) { /* Init board */ kk_board_init(); +#ifdef TWO_DISP + SSD1351_WriteString(0, 12, "firmware", Font_7x10, SSD1351_GREEN, SSD1351_BLACK); +#endif + /* Program the model into OTP, if we're not in screen-test mode, and it's * not already there */ diff --git a/tools/rle-dump/CMakeLists.txt b/tools/rle-dump/CMakeLists.txt index 5132118e3..1d7e63f9e 100644 --- a/tools/rle-dump/CMakeLists.txt +++ b/tools/rle-dump/CMakeLists.txt @@ -16,7 +16,7 @@ if(${KK_EMULATOR}) kkvariant.salt kkboard kktransport - trezorcrypto + hwcrypto kkrand -lc -lm) diff --git a/tools/variant/CMakeLists.txt b/tools/variant/CMakeLists.txt index 0a250e92e..dae9b45a3 100644 --- a/tools/variant/CMakeLists.txt +++ b/tools/variant/CMakeLists.txt @@ -1,3 +1,11 @@ +if("${COIN_SUPPORT}" STREQUAL "BTC") + return() +endif() + +if("${VARIANTS}" STREQUAL "NoObsoleteVariants") + return() +endif() + if(NOT ${KK_EMULATOR}) include_directories( ${CMAKE_SOURCE_DIR}/include diff --git a/unittests/board/CMakeLists.txt b/unittests/board/CMakeLists.txt index 81e294dec..a869b8bf1 100644 --- a/unittests/board/CMakeLists.txt +++ b/unittests/board/CMakeLists.txt @@ -5,7 +5,7 @@ set(sources include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) add_executable(board-unit ${sources}) target_link_libraries(board-unit @@ -17,7 +17,7 @@ target_link_libraries(board-unit kkvariant.keepkey kkvariant.salt kkboard - trezorcrypto + hwcrypto qrcodegenerator SecAESSTM32 kkemulator diff --git a/unittests/crypto/CMakeLists.txt b/unittests/crypto/CMakeLists.txt index 782549f28..119934228 100644 --- a/unittests/crypto/CMakeLists.txt +++ b/unittests/crypto/CMakeLists.txt @@ -5,20 +5,31 @@ set(sources include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) add_executable(crypto-unit ${sources}) -target_link_libraries(crypto-unit - gtest_main - kkfirmware - kkfirmware.keepkey - kkboard - kkboard.keepkey - kkvariant.keepkey + +set(libraries + gtest_main + kkfirmware + kkfirmware.keepkey + kkboard + kkboard.keepkey + kkvariant.keepkey + kkboard + kkemulator + qrcodegenerator + kkrand + hwcrypto + kktransport) + +if("${COIN_SUPPORT}" STREQUAL "BTC") +else() + list(APPEND libraries kkvariant.salt - kkboard - kkemulator - qrcodegenerator - kkrand - trezorcrypto - kktransport) + ) +endif() + +target_link_libraries(crypto-unit + ${libraries} + ) diff --git a/unittests/crypto/vuln1845.cpp b/unittests/crypto/vuln1845.cpp index 9abe4a6a4..28bf61bd2 100644 --- a/unittests/crypto/vuln1845.cpp +++ b/unittests/crypto/vuln1845.cpp @@ -1,9 +1,9 @@ #include extern "C" { -#include "trezor/crypto/segwit_addr.h" -#include "trezor/crypto/ecdsa.h" -#include "trezor/crypto/cash_addr.h" +#include "hwcrypto/crypto/segwit_addr.h" +#include "hwcrypto/crypto/ecdsa.h" +#include "hwcrypto/crypto/cash_addr.h" } #include "gtest/gtest.h" diff --git a/unittests/firmware/CMakeLists.txt b/unittests/firmware/CMakeLists.txt index 647d72571..c50f49eeb 100644 --- a/unittests/firmware/CMakeLists.txt +++ b/unittests/firmware/CMakeLists.txt @@ -1,20 +1,25 @@ set(sources coins.cpp - cosmos.cpp - eos.cpp - ethereum.cpp - nano.cpp recovery.cpp - ripple.cpp storage.cpp usb_rx.cpp u2f.cpp) +if("${COIN_SUPPORT}" STREQUAL "BTC") +else() + list(APPEND sources + cosmos.cpp + eos.cpp + ethereum.cpp + nano.cpp + ripple.cpp) +endif() + include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/lib/firmware ${CMAKE_BINARY_DIR}/include - ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-crypto) + ${CMAKE_SOURCE_DIR}/deps/crypto/hw-crypto) add_executable(firmware-unit ${sources}) target_link_libraries(firmware-unit @@ -27,8 +32,29 @@ target_link_libraries(firmware-unit kkvariant.salt kkboard kkemulator - trezorcrypto + hwcrypto qrcodegenerator SecAESSTM32 kkrand kktransport) + +# these nanopb files need to be removed and since this is the last binary built, they +# are set to be removed post-build. See lib/CMakeLists.txt for further explanation. +set(nanopb_rmsources + ${CMAKE_SOURCE_DIR}/lib/transport/pb_common.c + ${CMAKE_SOURCE_DIR}/lib/transport/pb_decode.c + ${CMAKE_SOURCE_DIR}/lib/transport/pb_encode.c) + +set(nanopb_rminc + ${CMAKE_SOURCE_DIR}/include/pb_common.h + ${CMAKE_SOURCE_DIR}/include/pb_decode.h + ${CMAKE_SOURCE_DIR}/include/pb_encode.h + ${CMAKE_SOURCE_DIR}/include/pb.h) + + add_custom_command(TARGET firmware-unit + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E rm ${nanopb_rmsources} + COMMAND + ${CMAKE_COMMAND} -E rm ${nanopb_rminc} + COMMAND_EXPAND_LISTS) diff --git a/unittests/firmware/coins.cpp b/unittests/firmware/coins.cpp index c11a59bd6..0be0a6f77 100644 --- a/unittests/firmware/coins.cpp +++ b/unittests/firmware/coins.cpp @@ -1,6 +1,8 @@ extern "C" { #include "keepkey/firmware/coins.h" +#ifndef BITCOIN_ONLY #include "keepkey/firmware/ethereum_tokens.h" +#endif // BITCOIN_ONLY } #include "gtest/gtest.h" @@ -74,7 +76,7 @@ TEST(Coins, TableSanity) { const auto &coin = coins[i]; if (!coin.has_contract_address) continue; - +#ifndef BITCOIN_ONLY const TokenType *token; if (!tokenByTicker(1, coin.coin_shortcut, &token)) { EXPECT_TRUE(false) << "Can't uniquely find " << coin.coin_shortcut; @@ -84,6 +86,7 @@ TEST(Coins, TableSanity) { EXPECT_TRUE(memcmp(coin.contract_address.bytes, token->address, coin.contract_address.size) == 0) << "Contract address mismatch for " << coin.coin_shortcut; +#endif // BITCOIN_ONLY } } @@ -134,6 +137,7 @@ TEST(Coins, BIP32AccountName) { 5, true, "Bitcoin Account #1\nChange Address #1"}, +#ifndef BITCOIN_ONLY {"Ethereum", {0x80000000 | 44, 0x80000000 | 60, 0x80000000 | 1, 0, 0}, 5, @@ -178,7 +182,9 @@ TEST(Coins, BIP32AccountName) { {0x80000000 | 44, 0x80000000 | 931, 0x80000000 | 69, 0, 0}, 5, true, - "MAYAChain Account #69"}}; + "MAYAChain Account #69"} +#endif // BITCOIN_ONLY + }; for (const auto &vec : vector) { char node_str[NODE_STRING_LENGTH]; @@ -200,6 +206,7 @@ TEST(Coins, BIP32AccountName) { } } +#ifndef BITCOIN_ONLY TEST(Coins, CoinByNameOrTicker) { const CoinType *ticker = coinByNameOrTicker("ZRX"); const CoinType *name = coinByNameOrTicker("0x"); @@ -221,3 +228,4 @@ TEST(Coins, TokenByChainAddress) { ASSERT_NE(zrx, nullptr); EXPECT_EQ(zrx->ticker, std::string(" ZRX")); } +#endif // BITCOIN_ONLY \ No newline at end of file diff --git a/unittests/firmware/cosmos.cpp b/unittests/firmware/cosmos.cpp index d150fda34..9f728ab81 100644 --- a/unittests/firmware/cosmos.cpp +++ b/unittests/firmware/cosmos.cpp @@ -3,7 +3,7 @@ extern "C" { #include "keepkey/firmware/cosmos.h" #include "keepkey/firmware/signtx_tendermint.h" #include "keepkey/firmware/tendermint.h" -#include "trezor/crypto/secp256k1.h" +#include "hwcrypto/crypto/secp256k1.h" } #include "gtest/gtest.h" diff --git a/unittests/firmware/ethereum.cpp b/unittests/firmware/ethereum.cpp index f8329a416..ecd33e9f9 100644 --- a/unittests/firmware/ethereum.cpp +++ b/unittests/firmware/ethereum.cpp @@ -1,5 +1,5 @@ extern "C" { -#include "trezor/crypto/address.h" +#include "hwcrypto/crypto/address.h" } #include "gtest/gtest.h" diff --git a/unittests/firmware/mayachain.cpp b/unittests/firmware/mayachain.cpp index 8a319a610..ebdfb7b29 100644 --- a/unittests/firmware/mayachain.cpp +++ b/unittests/firmware/mayachain.cpp @@ -2,7 +2,7 @@ extern "C" { #include "keepkey/firmware/coins.h" #include "keepkey/firmware/mayachain.h" #include "keepkey/firmware/tendermint.h" -#include "trezor/crypto/secp256k1.h" +#include "hwcrypto/crypto/secp256k1.h" } #include "gtest/gtest.h" diff --git a/unittests/firmware/recovery.cpp b/unittests/firmware/recovery.cpp index 654891c28..66768c61b 100644 --- a/unittests/firmware/recovery.cpp +++ b/unittests/firmware/recovery.cpp @@ -1,6 +1,6 @@ extern "C" { #include "keepkey/firmware/recovery_cipher.h" -#include "trezor/crypto/bip39_english.h" +#include "hwcrypto/crypto/bip39_english.h" } #include "gtest/gtest.h" diff --git a/unittests/firmware/ripple.cpp b/unittests/firmware/ripple.cpp index 155d057aa..7fcd3c688 100644 --- a/unittests/firmware/ripple.cpp +++ b/unittests/firmware/ripple.cpp @@ -2,7 +2,7 @@ extern "C" { #include "keepkey/firmware/coins.h" #include "keepkey/firmware/ripple.h" #include "keepkey/firmware/ripple_base58.h" -#include "trezor/crypto/hasher.h" +#include "hwcrypto/crypto/hasher.h" } #include "gtest/gtest.h" diff --git a/unittests/firmware/storage.cpp b/unittests/firmware/storage.cpp index b04af1ebe..02529554e 100644 --- a/unittests/firmware/storage.cpp +++ b/unittests/firmware/storage.cpp @@ -2,8 +2,8 @@ extern "C" { #include "keepkey/firmware/storage.h" #include "keepkey/firmware/policy.h" #include "keepkey/board/keepkey_board.h" -#include "trezor/crypto/memzero.h" -#include "trezor/crypto/aes/aes.h" +#include "hwcrypto/crypto/memzero.h" +#include "hwcrypto/crypto/aes/aes.h" #include "types.pb.h" #include "storage.h" } diff --git a/unittests/firmware/thorchain.cpp b/unittests/firmware/thorchain.cpp index 4fa8faa8f..707ece3da 100644 --- a/unittests/firmware/thorchain.cpp +++ b/unittests/firmware/thorchain.cpp @@ -2,7 +2,7 @@ extern "C" { #include "keepkey/firmware/coins.h" #include "keepkey/firmware/thorchain.h" #include "keepkey/firmware/tendermint.h" -#include "trezor/crypto/secp256k1.h" +#include "hwcrypto/crypto/secp256k1.h" } #include "gtest/gtest.h"