#!/usr/bin/env python3
#
# SPDX-License-Identifier: BSD-3-Clause
# Depthcharge: <https://github.com/nccgroup/depthcharge>
#
# Suppress complaints that don't add much value this script
#   pylint: disable=missing-module-docstring,missing-function-docstring
#   pylint: disable=invalid-name,redefined-outer-name

import sys
import traceback

from argparse import RawDescriptionHelpFormatter
from os.path import basename

from depthcharge import log
from depthcharge.cmdline import ArgumentParser, create_depthcharge_ctx
from depthcharge.string import xxd

_SCRIPT = basename(__file__)

_USAGE = """
{script:s} [options] -c <device config> -a <address> -l <length>
{script:s} [options] -c <device config> -a <address> -l <length> -f <file>
\r
""".format(script=_SCRIPT)

_DESCRIPTION = 'Read memory contents to a file or display them in a hex dump.'

_EPILOG = """
notes:
    If a filename is not provided, a textual hex dump will be printed.

    The following address and length suffixes are supported:

        * kB = 1000
        * K or kiB = 1024
        * MB = 1000 * 1000
        * M or MiB = 1024 * 1024
        * GB = 1000 * 1000 * 1000
        * G or GiB = 1024 * 1024 * 1024

    While optional, the use of the -c, --config option is STRONGLY ENCOURAGED.
    This will greatly speed up the execution time of this script, if it will
    be used multiple times for a given target. Similarly, once any payloads
    needed by a reader have been deployed to RAM, the -D, --skip-deploy
    option can be used to further speed up execution.

examples:
    Read 16384 bytes from 0x82000000 and display a hex dump:

      depthcharge-read-mem -c dev.cfg -a 0x82000000 -l 16384

    The following example is equivalent, but uses supported suffixes:

      depthcharge-read-mem -c dev.cfg -a 2080M  -l 16K

    This example instead reads data to a file. Note that Python's underscore
    syntax for readability is supported.

      depthcharge-read-mem -c dev.cfg -a 0x8200_0000 -l 16K -f data.bin

    Request Depthcharge to use the depthcharge.memory.SetexprMemoryReader class
    to read memory. Note that the `MemoryReader` suffix can be omitted, and
    that this argument is case-insensitve.

      depthcharge-read-mem -c dev.cfg -a 0x8200_0000 -l 1M -f data.bin --op setexpr
\r
"""


def handle_cmdline():
    supported = ArgumentParser.DEFAULT_ARGS + ['file', 'address', 'length', 'op']
    parser = ArgumentParser(init_args=supported,
                            formatter_class=RawDescriptionHelpFormatter,
                            usage=_USAGE, description=_DESCRIPTION, epilog=_EPILOG,
                            address_help='Target address to read from',
                            address_default=None,
                            length_required=True,
                            file_help='Optional file to store data in.')

    return parser.parse_args()


def read_memory(args):
    ctx = create_depthcharge_ctx(args)

    if args.file:
        ctx.read_memory_to_file(args.address, args.length, args.file, impl=args.op)
    else:
        data = ctx.read_memory(args.address, args.length, impl=args.op)
        hexdump = xxd(args.address, data)
        print(hexdump)


if __name__ == '__main__':
    args = handle_cmdline()
    try:
        read_memory(args)
    except Exception as error:  # pylint: disable=broad-except
        log.debug(traceback.format_exc())
        print('Error: ' + str(error), file=sys.stderr)
        sys.exit(1)
