/*++

Copyright (c) 1991-1993  Microsoft Corporation

Module Name:

    convdata.c

Abstract:

    RxpConvertDataStructures routine which converts 16- to 32-bit arrays of
    structures and vice versa.

Author:

    Richard Firth (rfirth) 03-Jul-1991

Revision History:

    03-Jul-1991 rfirth
        created
    15-Jul-1991 JohnRo
        Align each structure (e.g. in an array) if necessary.  This will,
        for instance, help print dest info level 1 handling.  Changed
        RxpConvertDataStructures to allow ERROR_MORE_DATA, e.g. for print APIs.
        Also, use DBG instead of DEBUG equate.
    17-Jul-1991 JohnRo
        Extracted RxpDebug.h from Rxp.h.
    07-Sep-1991 JohnRo
        Made changes suggested by PC-LINT.
    20-Nov-1991 JohnRo
        Clarify which routine an error message is from.
    21-Nov-1991 JohnRo
        Removed NT dependencies to reduce recompiles.
    18-May-1993 JohnRo
        DosPrintQGetInfoW underestimates number of bytes needed.
        Made changes suggested by PC-LINT 5.0
    21-Jun-1993 JohnRo
        RAID 14180: NetServerEnum never returns (alignment bug in
        RxpConvertDataStructures).

--*/



#include <windef.h>
#include <align.h>
#include <lmerr.h>
#include <rxp.h>                // My prototype.
#include <rxpdebug.h>           // IF_DEBUG().
#include <rap.h>
#include <netdebug.h>



NET_API_STATUS
RxpConvertDataStructures(
    IN  LPDESC  InputDescriptor,
    IN  LPDESC  OutputDescriptor,
    IN  LPDESC  InputAuxDescriptor OPTIONAL,
    IN  LPDESC  OutputAuxDescriptor OPTIONAL,
    IN  LPBYTE  InputBuffer,
    OUT LPBYTE  OutputBuffer,
    IN  DWORD   OutputBufferSize,
    IN  DWORD   PrimaryCount,
    OUT LPDWORD EntriesConverted OPTIONAL,
    IN  RAP_TRANSMISSION_MODE TransmissionMode,
    IN  RAP_CONVERSION_MODE ConversionMode
    )

/*++

Routine Description:

    A buffer containing 16- or 32-bit structures is converted to 32- or 16-bit
    structures resp. in a separate buffer. The structures may or may not have
    associated auxiliary structures. The output buffer is expected to be large
    enough to contain all the input data structures plus any variable length
    data items. Therefore, in the worst case, there must be enough space to
    convert all 16-bit items to 32-bits and convert ASCII strings to UNICODE.

    There may not be any auxiliary structures associated with the primaries,
    in which case the auxiliary descriptor pointers should BOTH BE NULL.

    Assumptions:

        IMPORTANT: The input buffer is assumed to have MEANINGFUL pointers.


Arguments:

    InputDescriptor - Pointer to string describing input primary data structure.

    OutputDescriptor - Pointer to string describing output primary data
        structure.

    InputAuxDescriptor - Pointer to string describing input auxiliary data
        structure.  May be NULL.

    OutputAuxDescriptor - Pointer to string describing output auxiliary data
        structure.  May be NULL.

    InputBuffer - Pointer to data area containing input structures.

    OutputBuffer - Pointer to data area where output structures will be placed.
        If the OutputBufferSize is too small, the contents of the output area
        is undefined.

    OutputBufferSize - Size of output buffer.

    PrimaryCount - Number of primary structures in InputBuffer.

    EntriesConverted - optionally points to a DWORD which will be filled-in
        with the number of entries actually converted.  This will be the same
        as PrimaryCount if we return NO_ERROR, but will be less if we return
        ERROR_MORE_DATA.

    TransmissionMode - Parameter to RapConvertSingleEntry.

    ConversionMode - Which 16- to 32-bit conversion to use.


Return Value:

    NET_API_STATUS - NERR_Success or ERROR_MORE_DATA.
        (BUGBUG: The callers of this routine assume these are the only two
        error codes.)

--*/

{
    NET_API_STATUS status;
    DWORD   input_structure_size;
    DWORD   output_structure_size;
    DWORD   input_aux_structure_size = 0;
    DWORD   output_aux_structure_size = 0;
    DWORD   input_alignment;
    DWORD   output_alignment;
    DWORD   input_aux_alignment = 0;
    DWORD   output_aux_alignment = 0;
    LPBYTE  aligned_input_buffer_start;
    LPBYTE  aligned_output_buffer_start;
    BOOL    auxiliaries = (InputAuxDescriptor != NULL);
    DWORD   aux_count_offset = 0;
    DWORD   aux_count = 0;
    DWORD   entries_fully_converted = 0;
    LPBYTE  next_input_structure;
    LPBYTE  next_output_structure;

    //
    // These next two variables are used by RapConvertSingleEntry which copies
    // stuff to the output buffer and informs us of the amount of space used or
    // required, depending on whether it has enough space to write the data; we
    // assume it will. RapConvertSingleEntry stores the fixed structures at the
    // head of the buffer and starts writing the variable parts (strings) at the
    // bottom. It uses variable_data_pointer as the next writable location for
    // the strings, and updates this variable as it writes to the area pointed to
    //

    LPBYTE  variable_data_pointer;
    DWORD   space_occupied = 0;

    //
    // if the conversion mode is NativeToRap or NativeToNative then the input
    // data is 32-bit. Same for output data
    //

    BOOL    input_is_32_bit = ConversionMode == NativeToRap || ConversionMode == NativeToNative;
    BOOL    output_is_32_bit = ConversionMode == RapToNative || ConversionMode == NativeToNative;


#if DBG
    //
    // the auxiliary data descriptors must be both NULL or both non-NULL
    //

    BOOL    aux_in, aux_out;

    aux_in = (InputAuxDescriptor != NULL);
    aux_out = (OutputAuxDescriptor != NULL);

    if (aux_in ^ aux_out)
    {
        NetpKdPrint(("RxpConvertDataStructures: "
                "InputAuxDescriptor & OutputAuxDescriptor out of sync\n"));
        NetpAssert(FALSE);
    }
#endif


    input_structure_size = RapStructureSize(InputDescriptor,
                                            TransmissionMode,
                                            input_is_32_bit
                                            );
    output_structure_size = RapStructureSize(OutputDescriptor,
                                            TransmissionMode,
                                            output_is_32_bit
                                            );
    input_alignment = RapStructureAlignment(InputDescriptor,
                                            TransmissionMode,
                                            input_is_32_bit
                                            );
    output_alignment = RapStructureAlignment(OutputDescriptor,
                                            TransmissionMode,
                                            output_is_32_bit
                                            );

    if (auxiliaries)
    {
        input_aux_structure_size = RapStructureSize(InputAuxDescriptor,
                                                    TransmissionMode,
                                                    input_is_32_bit
                                                    );
        output_aux_structure_size = RapStructureSize(OutputAuxDescriptor,
                                                    TransmissionMode,
                                                    output_is_32_bit
                                                    );
        input_aux_alignment = RapStructureAlignment(InputAuxDescriptor,
                                                    TransmissionMode,
                                                    input_is_32_bit
                                                    );
        output_aux_alignment = RapStructureAlignment(OutputAuxDescriptor,
                                                    TransmissionMode,
                                                    output_is_32_bit
                                                    );
        aux_count_offset = RapAuxDataCountOffset(InputDescriptor,
                                                TransmissionMode,
                                                input_is_32_bit
                                                );
    }

    //
    // Make sure first (only?) input and output structures are aligned.  (This
    // won't do anything for the RAP formats, but is critical for native.)
    //
    aligned_input_buffer_start = RapPossiblyAlignPointer(
            InputBuffer,
            input_alignment,
            input_is_32_bit);
    aligned_output_buffer_start = RapPossiblyAlignPointer(
            OutputBuffer,
            output_alignment,
            output_is_32_bit);

    //
    // We can't use the space we just skipped over, so update size accordingly.
    //
    NetpAssert( sizeof(DWORD) >= sizeof(LPBYTE) );
    OutputBufferSize
            -= ( ((DWORD)aligned_output_buffer_start) - ((DWORD)OutputBuffer) );
    NetpAssert( OutputBufferSize >= 1 );

    //
    // Initialize roving pointers.
    //
    next_input_structure  = aligned_input_buffer_start;
    next_output_structure = aligned_output_buffer_start;
    variable_data_pointer = aligned_output_buffer_start + OutputBufferSize;

    //
    // For each primary structure, copy the input primary to the output buffer,
    // changing format as we go; copy any associated variable data at the end
    // of the output buffer. Then, if there is an aux count associated with
    // the primary, do the same action for the auxiliary structures and
    // associated strings/variable data
    //

    while (PrimaryCount--)
    {
        //
        // Convert the data for this instance of the primary structure.
        //
       status = RapConvertSingleEntry(
                next_input_structure,
                InputDescriptor,        // input desc
                FALSE,                  // input ptrs NOT meaningless
                aligned_output_buffer_start,
                next_output_structure,
                OutputDescriptor,
                FALSE,                  // don't set offsets (want ptrs)
                &variable_data_pointer,
                &space_occupied,
                TransmissionMode,       // as supplied in parameters
                ConversionMode          // as supplied in parameters
                );
        NetpAssert( status == NERR_Success );

        if (space_occupied > OutputBufferSize)
        {
            IF_DEBUG(CONVDATA) {
                NetpKdPrint(("RxpConvertDataStructures: "
                        "output buffer size blown by primary\n"));
            }
            status = ERROR_MORE_DATA;
            goto Cleanup;
        }

        //
        // if we have auxiliary structs, pull out the number associated
        // with this primary struct from the primary struct itself
        // before pointing to the next copy location (this allows us to
        // handle the case where there are a variable number of aux
        // structs per each primary. There may not be such a case, but
        // this is defensive programming)
        //

        if (auxiliaries)
        {
            if (input_is_32_bit)
            {
                aux_count = *(LPDWORD)(next_input_structure + aux_count_offset);
            }
            else
            {
                // BUGBUG: use RapGetWord() macro here:
                aux_count = *(LPWORD)(next_input_structure + aux_count_offset);
            }
        }

        //
        // Bump to next element of each array (or just beyond end, if we're
        // done).
        //
        next_input_structure  += input_structure_size;
        next_output_structure += output_structure_size;

        //
        // Make sure each primary structure is aligned.  (This won't do anything
        // for the RAP formats, but is critical for native.)
        //
        next_input_structure = RapPossiblyAlignPointer(
                next_input_structure, input_alignment, input_is_32_bit);

        {
            DWORD NextOutputAlignment =
                    (LPBYTE) (RapPossiblyAlignPointer(
                            next_output_structure,
                                    output_alignment,
                                    output_is_32_bit))
                    -  next_output_structure;

            NetpAssert( NextOutputAlignment < ALIGN_WORST );
            if (NextOutputAlignment > 0) {
                next_output_structure += NextOutputAlignment;
                space_occupied        += NextOutputAlignment;
            }
        }

        //
        // use aux_count to determine whether loop should be performed
        //

        while (aux_count)
        {
            //
            // Convert the data for this instance of the secondary structure.
            //
            status = RapConvertSingleEntry(
                    next_input_structure,
                    InputAuxDescriptor,
                    FALSE,                  // input ptrs NOT meaningless
                    aligned_output_buffer_start,
                    next_output_structure,
                    OutputAuxDescriptor,
                    FALSE,                  // don't set offsets (want ptrs)
                    &variable_data_pointer,
                    &space_occupied,
                    TransmissionMode,       // as supplied in parameters
                    ConversionMode          // as supplied in parameters
                    );
            NetpAssert( status == NERR_Success );

            if (space_occupied > OutputBufferSize)
            {
                IF_DEBUG(CONVDATA) {
                    NetpKdPrint(("RxpConvertDataStructures: "
                            "output buffer size blown by secondary\n"));
                }
                status = ERROR_MORE_DATA;
                goto Cleanup;
            }

            next_input_structure += input_aux_structure_size;
            next_output_structure += output_aux_structure_size;
            --aux_count;

            //
            // Make sure next structure (if any) is aligned.  (This won't do
            // anything for the RAP formats, but is critical for native.)
            //
            next_input_structure = RapPossiblyAlignPointer(
                    next_input_structure,
                    input_aux_alignment,
                    input_is_32_bit);
            next_output_structure = RapPossiblyAlignPointer(
                    next_output_structure,
                    output_aux_alignment,
                    output_is_32_bit);
            space_occupied = RapPossiblyAlignCount(
                    space_occupied,
                    output_aux_alignment,
                    output_is_32_bit);

        } // while (aux_count)

        ++entries_fully_converted;

    } // while (primary_count--)

    status = NERR_Success;

Cleanup:

    if (EntriesConverted != NULL) {
        *EntriesConverted = entries_fully_converted;
    }

    return (status);
}
