SNIFFV (Sniff Viewer) is an enhanced version of SNIFFR (Sniff Reader) which
makes use of user defined templates to decode network packets and display
them in a more meaningful format.

Templates can be supplied in ASCII text (T=filename[.PTT]) in which case
SNIFFV will internally compile them into the binary form, or as a pre-compiled
binary image (B=filename[.PTB]). The latter will be slightly faster when
loading large templates.

For details on SNIFFV command line parameters, please run SNIFFV without any
command options. When viewing a capture, press F1 for a list of interactive
command keys.


Text template format:
---------------------
All templates are initially created in text form.  Please refer to the
included sample IP.PTT for an example of a text form template.

[]      = Indicates optional portion(s).
..      = Indicates that construct may be optionally repeated.
{o} = Arithmetic operator:
        +                   = Addition
        -                   = Subtraction
        *                   = Multiplication
        /                   = Division
        %                   = Modulus (remainder after division)
        &                   = Bitwise AND
        |                   = Bitwise OR
        ^                   = Bitwise Exclusive OR
        =                   = Test for equality
        !=                  = Test for inequality
        <                   = Test for Less Than
        >                   = Test for Greater Than
        <<                  = Shift left
        >>                  = Shift right
    *:  Arithmetic operations are applied without precedence, from left
        to right. Precedence can be forced by the use of () brackets.
{c} = Constant value (evaluated to 32-bits):
        0-9..               = Decimal number
        %0-1..              = Binary number
        @0-7..              = Octal number
        $0-9,A-F..          = Hexidecimal number
        -{c}                = Negative of value
        ~{c}                = Bitwize compliment of value
        !{c}                = Logical NOT of value
        ({c}[{o}{c}..])     = Nested expression (may have operators)
{d} = Simple constant decimal number (only)
{v} = Memory (from packet data) or Inline (constant) value:
        1<{c}               = 8-bit  L-endian value from address {c}    *1,2
        1>{c}               = 8-bit  B-endian value from address {c}    *1,2
        2<{c}               = 16-bit L-endian value from address {c}    *1
        2>{c}               = 16-bit B-endian value from address {c}    *1
        4<{c}               = 32-bit L-endian value from address {c}    *1
        4>{c}               = 32-bit B-endian value from address {c}    *1
        #{c}                = Inline constant value
        X                   = Content of temp-register 'X'
        Y                   = Content of temp-register 'Y'
        Z                   = Content of temp-register 'Z'
        {v}{o}{v}..         = Arithmetic operations                     *3
        ({v}[{o}{v}..])     = Nested expression (forced precedence).
    *1: If address {c} is positive, value is zero-extended (unsigned),
        if address {c} is negative, value is sign-extended (signed).
    *2: 8-bit values don't actually have "endian" - you can use either
        1<.. or 1>.. - both forms result in the same encoding.
    *3: Anywhere a {v} type value is expected, you may combine multiple
        {v} type values with arithmetic operations.
{r} = A destination temporary-register:
        X       = Assign to X temporary register
        Y       = Assign to Y temporary register
        Z       = Assign to Z temporary register
{f} = A display format specifier: [[-][0]{d1}]f[*{d2}s]
        -       = Left-justified, otherwise right-justified
        0       = Fill left-justified field with '0', otherwise space
        {d1}    = Width of field, if none or zero: free-format
        f:  D   = Signed decimal number
            U   = Unsigned decimal number
            B   = Binary number
            O   = Octal number
            X   = Hexidecimal number
            C   = Character
            S   = String (zero terminated)
        *       = Denotes that field repeats
        {d2}    = Number of times to repeat
        s       = Separator between repeated fields ('~'=none)

#section [|] [{v} ..]
    Begins a section definition, which describes the format of a block
    of packet data. All other constructs MUST occur within a section.

    ALL {v} parameters must evaluate TRUE (!0) to activate the section
    unless a leading '|' is specified in which case the section will be
    activated if ANY {v} parameter evaluates TRUE.

    Sections may be nested - sections occuring within sections will only
    activate if the containing section is active.

    If no parameters are provided, the section will only activate if no
    preceeding sections at the same level were activated. This is used
    to provide a "default" section to describe packet data that is not
    recognized as any other type.

    Sections are terminated with the #end directive.
    
    Sections contains text data which is output to the display, with the
    following special data substitutions available.
        ~{f}{v} = Output value in specified format.
        ~.      = Force a NEWLINE in output
        ~;      = Force a ';' in output (normally starts a comment)
        ~~      = Force a '~' in output (normally starts value output)
        ~=      = Stop expression scanner (insert nothing in output)
        ~       = At end of line - suppress NEWLINE

#data {v} ..

    Prepares a "data block" which is used by some other operations.

    Inline (constant) values output only as many bytes as are required
    to fit the value - use separate #0 values to create leading zeros.
        eg: #$1234  = Outputs 12 34
            #$0056  = Outputs 56

    Memory (from packet) values output as many bytes as the size of the
    memory item (first item in the case of operators).

    Values are output in "Big Endian" (most-significant bytes first).
    You can change the endian of the source value to reverse this for
    memory items.

#set {r} {v}

    Assigns a value to "temporary-register" X, Y or Z. This can be
    useful to:
        - Perform a calculation only once and use the result multiple
          times.
        - Pass a value on to forward template description. *1

    *1: At the beginning of each section, the 'Z' register is always set
        to the total size of the packet. This is needed to calculate the
        size of the packet data in some cases, however it also means that
        you cannot use 'Z' to pass values forward to other sections.

#check {r} {d} {v1} [{v2}] [{v3} {v4} ..]

    Calculates a check value used to validate certain data structures.

    If a #data block is defined, the check value is calculated over
    it (and it is cleared) before any other address/size pairs are
    processed. This is used to handle "pseudo-headers" required in
    some protocol check calculations.

    {r}             = Register to receive calculated check value
    {d}             = Type of check value:
                        1 = 8-bit sum
                        2 = 8-bit XOR
                        3 = 16-bit Big-Endian 1's compliment
                        4 = 16-bit Big-Endian 2's compliment
                        5 = 16-bit Little-Endian 1's compliment
                        6 = 16-bit Little-Endian 2's compliment
                        7 = Cyclic Redundancy Check     ({v2} req'd)
    {v1}            = Value to initialize check calculation.
    {v2}            = Optional value required for some check types:
                        7:  CRC generator polynomial
                      (must NOT be provided unless required)
    [{v3} {v4}..]   = Data block Address/size pairs to include in the
                      check calculation.

#if {v} [..]

    Conditionally processes a portion of the template description.

    The value {v} controls the conditional processing. FALSE (0) means
    to skip (not-process) this #if block - TRUE (!0) means to process
    the #if block.

    If [..] template data occurs on the same line, then a "one line #if"
    is recognzed - only the template data on the rest of the line is
    conditionally processed - unconditional processing resumes with the
    following line.

    If no [..] template data occurs on the same line, then a "multi line
    #if" is recognzed - All template data up until a #else or #end directive
    is conditionally processed.

#else [..]

    Conditionally processes a portion of the template description only
    if the corresponding #if directive was FALSE (did not process).

    Only a "multi line #if" can have an #else clause.

    #else can be "one line" or "multi line" - see #if.

#switch {v} [..]

    Conditionally processes one of several sections of the template
    description based on the {v} value. The section to be processed is
    selected based on the following #case directives.

    The template data between #switch and the first #case directive
    is processed as a default, only if no #case directives matched
    the switch value.

    #switch and all of it's #case directves must be followed by a #end
    directive, after which unconditional processing resumes.

    For convience, template data [..] may appear on the same line as
    #switch

#case {v} [..]

    Selects a block of template data to be processed following a #switch
    directive. If {v} matches the {v} originally supplied to #switch, the
    block of data is processed, then processing continues after the #end
    directive corresponding to the #switch.

    If no match is found, the next #case block is checked. If #end is
    reached before any match is found, the #switch default block is
    processed.

    For convience, template data [..] may appear on the same line as
    #case

#offset [+/-]{v}

    At the beginning of any section the Memory addresses used for {v}
    are relative to the beginning of the packet (offset 0).

    The #offset directive allows you to apply an offset to the memory
    address calculations which cam be used to:
    - Handle sections which are not located at an absolute offset
      into the packet.
    - Make it easier to code memory addresses relative to a fixed
      position.

    The new offset may be specified with '+' or '-' in which case the
    {v} value is added(+) or subtracted(-) from the current offset.

    If neither '+' or '-' is used, the {v} value replaces the current
    offset.

#dump {v1} {v2} {v3}

    Peforms a "memory dump" of a block of packet data:
        {v1}    = Address where dump is to begin.
        {v2}    = Number of bytes to dump.
        {v3}    = Maximum number of lines to use on screen.

    If the {v2} number of bytes to dump is more than can be displayed in
    {v3} maximum lines on screen, then the dump field becomes scrollable
    allowing you to view the entire content.

    If either {v2} or {v3} evaluates to zero, then no dump is performed.

#end

    This directive is used to close:
        #section definitions
        multi-line #if/#else blocks
        #switch/#case blocks


Binary template format:
-----------------------
When compiled into SNIFFVs internal binary representation, the template
data follows this format:

    Section header:
    ---------------
    1-0xFF      = Start of section indicator
    1-n         = Number of match parameters (bit7 == '|')
    2-size      = Total size of section (incl. header)
    n*{v}       = Match parameters
    .. template data ...

    Data within section:
    --------------------
    0xxxxxxx                            = ASCII data to output
    10frtttt [fmt] [rpt [sep]] {mem}    = Display value
        f = 1 if format provided:   fmt= lzwwwwww
            l = Left justify
            z = Zero fill
            w = field-width-1
        r = 1 if repeat provided:   rpt= snnnnnnn [sep]
            s = Separator provided
            n = # times to repeat
        tttt =  0   = Hex
                1   = bin
                2   = Octal
                3   = U-decimal
                4   = S-decimal
                5   = Character
                6   = String
    110xxxxx                            = Not currently used
    111xxxxx                            = Special function
        E0 {v}                          = X = {v}
        E1 {v}                          = Y = {v}
        E2 {v}                          = Z = {v}
        E3 t {v1} [{v2}] n [v3 v4]..    = X = check calculation
        E4 t {v1} [{v2}] n [v3 v4]..    = X = check calculation
        E5 t {v1} [{v2}] n [v3 v4]..    = X = check calculation
            t       = Type of check to calculate
            {v1}    = Initial value
            {v2}    = CRC polynomial (t==7 only)
            n       = Number of address/size pairs
            {v3}    = Address for block check
            {v4}    = size of block
        E6 {v}                          = Offset = {v}
        E7 {v}                          = Offset = Offset + {v}
        E8 {v}                          = Offset = Offset - {v}
        E9 n {v}..                      = Create data buffer
            n   = Number of values to process
            {v} = Value to insert
        EA {v1} {v2} {v3}               = Dump memory block
            {v1}    = Address to begin dump
            {v2}    = Size of dump area in bytes
            {v3}    = Maximum # lines to use on screen
        EB {v} n                        = Conditional jump
            {v}     = Test value
            n       = 00-FF = 1-256 bytes to skip if {v}==0
        EC n                            = unconditinal jump
            n       = 00-FF = 1-256 bytes to skip
        ED {v} addr len                 = #switch (begin)
            {v}     = Value to test
            addr    = 16-bit(LE) final address
            n       = 00-FF = 1-256 bytes in "default" block
        EE {v} n                        = #switch (#case)
            {v}     = Value to match
            n       = 00-FF = 1-256 bytes to next block
        EF                              = #switch (end)
        FF                              = Start of section

    Memory value {v} encoding:
        --High Nibble--                         --Low Nibble--
        0000 8u         - 8-bit unsigned        0000 = No operation
        0001 8s         - 8-bit signed          0001 = +
        0010 16lu       - 16-bit LE unsigned    0010 = -
        0011 16ls       - 15-bit LE signed      0011 = *
        0100 16bu       - 16-bit BE unsigned    0100 = /
        0101 16bs       - 16-bit BE signed      0101 = %
        0110 32l        - 32-bit LE             0110 = &
        0111 32b        - 32-bit BE             0111 = |
        1000 Imm8+      - Imm 8-bit unsigned    1000 = ^
        1001 imm8-      - Imm 8-bit signed      1001 = <
        1010 imm16+     - Imm 16-bit unsigned   1010 = >
        1011 imm16-     - Imm 16-bit signed     1011 = <<
        1100 imm32      - Imm 32-bit            1100 = >>
        1101 reg0       - Content of X          1101 = ==
        1110 reg1       - Content of Y          1110 = !=
        1111 reg3       - Content of Z          1111 = Force Precedence
    Types 0000-0111 denote memory accesses and are followed by
    an address: 0xxxxxxx            = 0-127
                1xxxxxxx xxxxxxxx   = 128-32895
    Types 1000-1100 denote immediate values:
        8-bit:  00-FF               = 0-255             (+ OR -)
        16-bit: 0000-FFFF           = 256-65,791        (+ OR -)
        32-bit: 00000000-FFFFFFFF   = 0-4,294,967,295   (+ AND -)
    If low-nibble indicates an operation, second memory operand
    is processed, and operation is performed - this continues
    until "No operation" is discovered.
        eg: #1+#2*#3+#4     = 81 01 83 02 81 03 80 04       = 13
    If lower nibble is '1111' then upper nibble is a count(-1)
    of operations to be grouped together.
        eg: #1+(#2*#3)+#4   = 81 01 1F 83 02 81 03 80 04    = 11
    Note that it is better to perform constant calculations
    within a constant expression, as no additional template data
    will be generated:
        eg: 2>12+(#1+#3)    = 41 0C 1F 81 01 80 02
            2>12+#(1+2)     = 41 0C 80 03


Template debugging:
-------------------
SNIFFV tries to "catch" template errors when it compiles the template,
however some errors will only occur while the template is being processed
when this happens, SNIFFV terminates with an error message showing the
address within the binary template which was being processed.

To assist you in determine which text source lines represent the problem
area, the '/A' option can be used to obtain information about the addresses
within the binary template corresponding to text lines.

When loading a T=filename[.PTT] text template, the '/A' option causes
the template to be written to standard output with each line prefixed by a
line number and address within the binary template. You can redirect this
to a file for reference: SNIFFV T=IP /A >IP.TXT

When converting a binary template into a text template (*) the '/A' option
causes each line in the created text template to be prefixed with the
line number and address within the binary template.

(*) while it is possible to recreate a text template from a binary image,
it is better to save the original text template. All comments and any source
formatting which does not affect the output will be lost during the
text->binary->text conversion process.


SNIFFD (Sniff Dumper) is a command line tool that displays decoded packets
on standard output (can be redirected to a file). It uses the same templates
and options as SNIFFV, however it has a few additional command line options:

When no template (T= or B=) is supplied or section match within the supplied
template occurs, SNIFFD shows a hex dump of the data portion of the packet -
the /F command line option causes this hex dump to show the full packet
(including the packet header).

The L=n option can be used to limit the number of lines shown in hex dumps.
Use L=0 (or: L=) to use the number of lines encoded in the template.

The P=n[,n] causes SNIFFD to dump only a single packet or a range of packets.

