/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "Licence").
 * You may not use this file except in compliance with the Licence.
 *
 * You can obtain a copy of the licence at RISC OS path @.^.LICENCE
 * or  http://www.riscosdev.com/lanman98/LICENCE.CDDL
 * See the Licence for the specific language governing permissions
 * and limitations under the Licence.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the Licence file. If applicable, add the
 * following below this CDDL HEADER, with the fields enclosed by
 * brackets "[]" replaced with your own identifying information:
 * Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
 
/*
 *   Copyright 1996 Warm Silence Software Ltd.  All rights reserved.
 *   Use is subject to license terms.
 */

/*   PHBG 19/12/96: Initial version
 */

#include <string.h>
#include "date.h"
#include "word.h"
#include "LanMan98BaseLib/error.h"
#include "mb.h"

typedef struct
{
    int smb_idf;
    char smb_com;
    char smb_rcls;
    char smb_reh;
    word16_t smb_err;
    char flags;
    word16_t flags2;
    word16_t smb_res[6];
    word16_t smb_tid;
    word16_t smb_pid;
    word16_t smb_uid;
    word16_t smb_mid;
} header_t;

/*
    Everything here depends on this laying out as the 32 bytes on the wire,
    with the NT status occupying smb_rcls, smb_reh and smb_err.  Fails to
    compile rather than misreading the wire if a member is ever padded.
*/
typedef char header_is_the_size_of_the_wire_header[
                 (sizeof(header_t) == HDRSIZE) ? 1 : -1];

void MBInit(data_t mb)
{
    header_t *hdr;

    hdr = (header_t *) mb.buf;
    memset(hdr, 0, sizeof(*hdr));
    hdr->smb_idf = 0x424D53FF;
    SetWord16(hdr->smb_tid, 1);
    SetWord16(hdr->smb_pid, 1);
    SetWord16(hdr->smb_uid, 1);
    SetWord16(hdr->smb_mid, 1);
}

void MBSetCom(data_t mb, int com)
{
    ((header_t *)mb.buf)->smb_com = com;
}

void MBSetTid(data_t mb, int tid)
{
    SetWord16(((header_t *)mb.buf)->smb_tid, tid);
}

void MBSetUid(data_t mb, int uid)
{
    SetWord16(((header_t *)mb.buf)->smb_uid, uid);
}

int MBGetUid(data_t mb)
{
    return Word16(((header_t *)mb.buf)->smb_uid);
}

int MBError(data_t mb)
{
    if(MBIsNTStatus(mb))
        return MBGetNTStatus(mb) != 0;
    return ((header_t *)mb.buf)->smb_rcls;
}

void MBSetVWV(data_t mb, int n, ushort vwv[])
{
    char *p;

    p = mb.buf;
    p += sizeof(header_t);
    *p++ = n;
    if(n) memcpy(p, vwv, n * sizeof(short));
    p += n * sizeof(short);
    SetWord16(p, 0);
}

void MBAddDialect(data_t mb, char *dia) /* Check overflow */
{
    char *p, *pp;
    int n, len;

    p = mb.buf;
    p += sizeof(header_t);
    n = *p++;
    p += n * sizeof(short);
    n = Word16(p);
    pp = p + sizeof(word16_t);
    pp[n++] = DIALECT;
    len = strlen(dia)+1;
    memcpy(&pp[n], dia, len);
    n += len;
    SetWord16(p, n);
}

void MBAddAscii(data_t mb, char *dia)
{
    char *p, *pp;
    int n, len;

    p = mb.buf;
    p += sizeof(header_t);
    n = *p++;
    p += n * sizeof(short);
    n = Word16(p);
    pp = p + sizeof(word16_t);
    pp[n++] = ASCII;
    len = strlen(dia)+1;
    memcpy(&pp[n], dia, len);
    n += len;
    SetWord16(p, n);
}

void MBContinueAscii(data_t mb, char *dia)
{
    char *p, *pp;
    int n, len;

    p = mb.buf;
    p += sizeof(header_t);
    n = *p++;
    p += n * sizeof(short);
    n = Word16(p);
    pp = p + sizeof(word16_t);
    --n;
    len = strlen(dia)+1;
    memcpy(&pp[n], dia, len);
    n += len;
    SetWord16(p, n);
}

void MBAddVarBlk(data_t mb, int size, void *data)
{
    char *p, *pp;
    int n;

    p = mb.buf;
    p += sizeof(header_t);
    n = *p++;
    p += n * sizeof(short);
    n = Word16(p);
    pp = p + sizeof(word16_t);
    pp[n++] = VARBLK;
    SetWord16(&pp[n], size);
    n += sizeof(word16_t);
    memcpy(&pp[n], data, size);
    n += size;
    SetWord16(p, n);
}

void MBAddData(data_t mb, int size, void *data)
{
    char *p, *pp;
    int n;

    p = mb.buf;
    p += sizeof(header_t);
    n = *p++;
    p += n * sizeof(short);
    n = Word16(p);
    pp = p + sizeof(word16_t);
    pp[n++] = DATA;
    SetWord16(&pp[n], size);
    n += sizeof(word16_t);
    if(size) memcpy(&pp[n], data, size);
    n += size;
    SetWord16(p, n);
}

void MBAddRaw(data_t mb, int size, void *data)
{
    char *p, *pp;
    int n;

    p = mb.buf;
    p += sizeof(header_t);
    n = *p++;
    p += n * sizeof(short);
    n = Word16(p);
    pp = p + sizeof(word16_t);
    memcpy(&pp[n], data, size);
    n += size;
    SetWord16(p, n);
}

void MBGetVWV(data_t mb, int n, ushort vwv[])
{
    char *p;
    int actual_n;

    p = mb.buf;
    p += sizeof(header_t);
    actual_n = *p++;
    if(actual_n < n)
        Error("Data fields missing in reply");
    memcpy(vwv, p, n * sizeof(short));
}

char *MBGetRawBuf(data_t mb, int *size)
{
    char *p;
    int n;

    p = mb.buf;
    p += sizeof(header_t);
    n = *p++;
    p += n * sizeof(short);
    if (size) *size = Word16(p);
    p += sizeof(word16_t);
    return p;
}

void MBGetBuf(data_t mb, smb_buf_t *smb_buf)
{
    char *p;
    int n, room;

    p = mb.buf;
    p += sizeof(header_t);
    n = *p++;
    p += n * sizeof(short);
    room = Word16(p);
    p += sizeof(word16_t);
    if(room == 0)
    {
        smb_buf->type = 0;
        return;
    }
    smb_buf->type = *p++;
    switch(smb_buf->type)
    {
        case VARBLK:
        case DATA:
            smb_buf->size = Word16(p);
            p += sizeof(word16_t);
    }
    smb_buf->data = p;
}

int MBBufType(smb_buf_t *smb_buf)
{
    return smb_buf->type;
}

int MBBufSize(smb_buf_t *smb_buf)
{
    return smb_buf->size;
}

char *MBBufData(smb_buf_t *smb_buf)
{
    return smb_buf->data;
}

int MBMessLen(data_t mb)
{
    int len;

    len = sizeof(header_t);
    len += 1 + mb.buf[len] * sizeof(word16_t);
    len += 2 + Word16(mb.buf + len);
    return len;
}

void MBClearError(data_t mb)
{
    header_t *hdr;
    hdr = (header_t *) mb.buf;
    hdr->smb_rcls = 0;
    hdr->smb_reh = 0;
    SetWord16(hdr->smb_err, 0);
}

int MBDataOffset(int nwords)
{
    /* header, the word count byte, the words themselves, the byte count */
    return HDRSIZE + 1 + (2 * nwords) + 2;
}

char *MBAt(data_t mb, int offset, int length)
{
    if((offset < HDRSIZE) || (length < 0) || (offset + length > mb.size))
        Error("Message offset out of range: %d+%d in %d", offset, length,
              mb.size);
    return mb.buf + offset;
}

int MBIsNTStatus(data_t mb)
{
    header_t *hdr;
    hdr = (header_t *) mb.buf;
    return (Word16(hdr->flags2) & SMB_FLAGS2_NT_STATUS) != 0;
}

unsigned int MBGetNTStatus(data_t mb)
{
    header_t *hdr;
    hdr = (header_t *) mb.buf;
    /* The status occupies smb_rcls, smb_reh and smb_err, little endian and
       not word aligned.  Word32 reads it a byte at a time, which is what
       this has to be on the ARMs that fault an unaligned load. */
    return (unsigned int) Word32(&hdr->smb_rcls);
}

void MBSetFlags(data_t mb, int f1, int f2)
{
    header_t *hdr;
    hdr = (header_t *) mb.buf;
    hdr->flags = f1;
    SetWord16(hdr->flags2, f2);
}

void MBGetError(data_t mb, int *error, int *sub_error)
{
    header_t *hdr;
    hdr = (header_t *) mb.buf;
    *error = hdr->smb_rcls;
    *sub_error = Word16(hdr->smb_err);
}
