code:
/**************************************************************************
 *
 * Copyright 2013-2014 RAD Game Tools and Valve Software
 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
 * All Rights Reserved.
 *
 * 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.
 *
 **************************************************************************/

#include "miniz.h"

#ifndef MINIZ_NO_INFLATE_APIS

#ifdef __cplusplus
extern "C" {
#endif

/* ------------------- Low-level Decompression (completely independent from all compression API's) */

#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
#define TINFL_MEMSET(p, c, l) memset(p, c, l)

#define TINFL_CR_BEGIN  \
    switch (r->m_state) \
    {                   \
        case 0:
#define TINFL_CR_RETURN(state_index, result) \
    do                                       \
    {                                        \
        status = result;                     \
        r->m_state = state_index;            \
        goto common_exit;                    \
        case state_index:;                   \
    }                                        \
    MZ_MACRO_END
#define TINFL_CR_RETURN_FOREVER(state_index, result) \
    do                                               \
    {                                                \
        for (;;)                                     \
        {                                            \
            TINFL_CR_RETURN(state_index, result);    \
        }                                            \
    }                                                \
    MZ_MACRO_END
#define TINFL_CR_FINISH }

#define TINFL_GET_BYTE(state_index, c)                                                                                \
    do                                                                                \
    {                                                                                \
        while (pIn_buf_cur >= pIn_buf_end)                                                                                \
        {                                                                                \
            TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \
        }                                                                                \
        c = *pIn_buf_cur++;                                                                                \
    }                                                                                \
    MZ_MACRO_END

#define TINFL_NEED_BITS(state_index, n)                \
    do                                                 \
    {                                                  \
        mz_uint c;                                     \
        TINFL_GET_BYTE(state_index, c);                \
        bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \
        num_bits += 8;                                 \
    } while (num_bits < (mz_uint)(n))
#define TINFL_SKIP_BITS(state_index, n)      \
    do                                       \
    {                                        \
        if (num_bits < (mz_uint)(n))         \
        {                                    \
            TINFL_NEED_BITS(state_index, n); \
        }                                    \
        bit_buf >>= (n);                     \
        num_bits -= (n);                     \
    }                                        \
    MZ_MACRO_END
#define TINFL_GET_BITS(state_index, b, n)    \
    do                                       \
    {                                        \
        if (num_bits < (mz_uint)(n))         \
        {                                    \
            TINFL_NEED_BITS(state_index, n); \
        }                                    \
        b = bit_buf & ((1 << (n)) - 1);      \
        bit_buf >>= (n);                     \
        num_bits -= (n);                     \
    }                                        \
    MZ_MACRO_END

/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */
/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */
/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */
/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */
#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree)                    \
    do                                                                         \
    {                                                                          \
        temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)];                \
        if (temp >= 0)                                                         \
        {                                                                      \
            code_len = temp >> 9;                                              \
            if ((code_len) && (num_bits >= code_len))                          \
                break;                                                         \
        }                                                                      \
        else if (num_bits > TINFL_FAST_LOOKUP_BITS)                            \
        {                                                                      \
            code_len = TINFL_FAST_LOOKUP_BITS;                                 \
            do                                                                 \
            {                                                                  \
                temp = pTree[~temp + ((bit_buf >> code_len++) & 1)];           \
            } while ((temp < 0) && (num_bits >= (code_len + 1)));              \
            if (temp >= 0)                                                     \
                break;                                                         \
        }                                                                      \
        TINFL_GET_BYTE(state_index, c);                                        \
        bit_buf |= (((tinfl_bit_buf_t)c) << num_bits);                         \
        num_bits += 8;                                                         \
    } while (num_bits < 15);

/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */
/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */
/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */
/* The slow path is only executed at the very end of the input buffer. */
/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */
/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */
#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree)                                                                         \
    do                                                                                \
    {                                                                                \
        int temp;                                                                                \
        mz_uint code_len, c;                                                                                \
        if (num_bits < 15)                                                                                \
        {                                                                                \
            if ((pIn_buf_end - pIn_buf_cur) < 2)                                                                                \
            {                                                                                \
                TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree);                                                                \
            }                                                                                \
            else                                                                                \
            {                                                                                \
                bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \
                pIn_buf_cur += 2;                                                                                \
                num_bits += 16;                                                                                \
            }                                                                                \
        }                                                                                \
        if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)                                                          \
            code_len = temp >> 9, temp &= 511;                                                                                \
        else                                                                                \
        {                                                                                \
            code_len = TINFL_FAST_LOOKUP_BITS;                                                                                \
            do                                                                                \
            {                                                                                \
                temp = pTree[~temp + ((bit_buf >> code_len++) & 1)];                                                                \
            } while (temp < 0);                                                                                \
        }                                                                                \
        sym = temp;                                                                                \
        bit_buf >>= code_len;                                                                                \
        num_bits -= code_len;                                                                                \
    }                                                                                \
    MZ_MACRO_END

tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
{
    static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 };
    static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };
    static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 };
    static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
    static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
    static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 };

    mz_int16 *pTrees[3];
    mz_uint8 *pCode_sizes[3];

    tinfl_status status = TINFL_STATUS_FAILED;
    mz_uint32 num_bits, dist, counter, num_extra;
    tinfl_bit_buf_t bit_buf;
    const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
    mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL;
    size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;

    /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */
    if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start))
    {
        *pIn_buf_size = *pOut_buf_size = 0;
        return TINFL_STATUS_BAD_PARAM;
    }

    pTrees[0] = r->m_tree_0;
    pTrees[1] = r->m_tree_1;
    pTrees[2] = r->m_tree_2;
    pCode_sizes[0] = r->m_code_size_0;
    pCode_sizes[1] = r->m_code_size_1;
    pCode_sizes[2] = r->m_code_size_2;

    num_bits = r->m_num_bits;
    bit_buf = r->m_bit_buf;
    dist = r->m_dist;
    counter = r->m_counter;
    num_extra = r->m_num_extra;
    dist_from_out_buf_start = r->m_dist_from_out_buf_start;
    TINFL_CR_BEGIN

    bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0;
    r->m_z_adler32 = r->m_check_adler32 = 1;
    if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
    {
        TINFL_GET_BYTE(1, r->m_zhdr0);
        TINFL_GET_BYTE(2, r->m_zhdr1);
        counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
        if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
            counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
        if (counter)
        {
            TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED);
        }
    }

    do
    {
        TINFL_GET_BITS(3, r->m_final, 3);
        r->m_type = r->m_final >> 1;
        if (r->m_type == 0)
        {
            TINFL_SKIP_BITS(5, num_bits & 7);
            for (counter = 0; counter < 4; ++counter)
            {
                if (num_bits)
                    TINFL_GET_BITS(6, r->m_raw_header[counter], 8);
                else
                    TINFL_GET_BYTE(7, r->m_raw_header[counter]);
            }
            if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8))))
            {
                TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED);
            }
            while ((counter) && (num_bits))
            {
                TINFL_GET_BITS(51, dist, 8);
                while (pOut_buf_cur >= pOut_buf_end)
                {
                    TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT);
                }
                *pOut_buf_cur++ = (mz_uint8)dist;
                counter--;
            }
            while (counter)
            {
                size_t n;
                while (pOut_buf_cur >= pOut_buf_end)
                {
                    TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT);
                }
                while (pIn_buf_cur >= pIn_buf_end)
                {
                    TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS);
                }
                n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
                TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n);
                pIn_buf_cur += n;
                pOut_buf_cur += n;
                counter -= (mz_uint)n;
            }
        }
        else if (r->m_type == 3)
        {
            TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
        }
        else
        {
            if (r->m_type == 1)
            {
                mz_uint8 *p = r->m_code_size_0;
                mz_uint i;
                r->m_table_sizes[0] = 288;
                r->m_table_sizes[1] = 32;
                TINFL_MEMSET(r->m_code_size_1, 5, 32);
                for (i = 0; i <= 143; ++i)
                    *p++ = 8;
                for (; i <= 255; ++i)
                    *p++ = 9;
                for (; i <= 279; ++i)
                    *p++ = 7;
                for (; i <= 287; ++i)
                    *p++ = 8;
            }
            else
            {
                for (counter = 0; counter < 3; counter++)
                {
                    TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]);
                    r->m_table_sizes[counter] += s_min_table_sizes[counter];
                }
                MZ_CLEAR_ARR(r->m_code_size_2);
                for (counter = 0; counter < r->m_table_sizes[2]; counter++)
                {
                    mz_uint s;
                    TINFL_GET_BITS(14, s, 3);
                    r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s;
                }
                r->m_table_sizes[2] = 19;
            }
            for (; (int)r->m_type >= 0; r->m_type--)
            {
                int tree_next, tree_cur;
                mz_int16 *pLookUp;
                mz_int16 *pTree;
                mz_uint8 *pCode_size;
                mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16];
                pLookUp = r->m_look_up[r->m_type];
                pTree = pTrees[r->m_type];
                pCode_size = pCode_sizes[r->m_type];
                MZ_CLEAR_ARR(total_syms);
                TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0]));
                TINFL_MEMSET(pTree, 0, r->m_table_sizes[r->m_type] * sizeof(pTree[0]) * 2);
                for (i = 0; i < r->m_table_sizes[r->m_type]; ++i)
                    total_syms[pCode_size[i]]++;
                used_syms = 0, total = 0;
                next_code[0] = next_code[1] = 0;
                for (i = 1; i <= 15; ++i)
                {
                    used_syms += total_syms[i];
                    next_code[i + 1] = (total = ((total + total_syms[i]) << 1));
                }
                if ((65536 != total) && (used_syms > 1))
                {
                    TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
                }
                for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
                {
                    mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index];
                    if (!code_size)
                        continue;
                    cur_code = next_code[code_size]++;
                    for (l = code_size; l > 0; l--, cur_code >>= 1)
                        rev_code = (rev_code << 1) | (cur_code & 1);
                    if (code_size <= TINFL_FAST_LOOKUP_BITS)
                    {
                        mz_int16 k = (mz_int16)((code_size << 9) | sym_index);
                        while (rev_code < TINFL_FAST_LOOKUP_SIZE)
                        {
                            pLookUp[rev_code] = k;
                            rev_code += (1 << code_size);
                        }
                        continue;
                    }
                    if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)]))
                    {
                        pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next;
                        tree_cur = tree_next;
                        tree_next -= 2;
                    }
                    rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
                    for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
                    {
                        tree_cur -= ((rev_code >>= 1) & 1);
                        if (!pTree[-tree_cur - 1])
                        {
                            pTree[-tree_cur - 1] = (mz_int16)tree_next;
                            tree_cur = tree_next;
                            tree_next -= 2;
                        }
                        else
                            tree_cur = pTree[-tree_cur - 1];
                    }
                    tree_cur -= ((rev_code >>= 1) & 1);
                    pTree[-tree_cur - 1] = (mz_int16)sym_index;
                }
                if (r->m_type == 2)
                {
                    for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);)
                    {
                        mz_uint s;
                        TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2);
                        if (dist < 16)
                        {
                            r->m_len_codes[counter++] = (mz_uint8)dist;
                            continue;
                        }
                        if ((dist == 16) && (!counter))
                        {
                            TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
                        }
                        num_extra = "\02\03\07"[dist - 16];
                        TINFL_GET_BITS(18, s, num_extra);
                        s += "\03\03\013"[dist - 16];
                        TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s);
                        counter += s;
                    }
                    if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
                    {
                        TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
                    }
                    TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]);
                    TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
                }
            }
            for (;;)
            {
                mz_uint8 *pSrc;
                for (;;)
                {
                    if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
                    {
                        TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0);
                        if (counter >= 256)
                            break;
                        while (pOut_buf_cur >= pOut_buf_end)
                        {
                            TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT);
                        }
                        *pOut_buf_cur++ = (mz_uint8)counter;
                    }
                    else
                    {
                        int sym2;
                        mz_uint code_len;
#if TINFL_USE_64BIT_BITBUF
                        if (num_bits < 30)
                        {
                            bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits);
                            pIn_buf_cur += 4;
                            num_bits += 32;
                        }
#else
                        if (num_bits < 15)
                        {
                            bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);
                            pIn_buf_cur += 2;
                            num_bits += 16;
                        }
#endif
                        if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
                            code_len = sym2 >> 9;
                        else
                        {
                            code_len = TINFL_FAST_LOOKUP_BITS;
                            do
                            {
                                sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)];
                            } while (sym2 < 0);
                        }
                        counter = sym2;
                        bit_buf >>= code_len;
                        num_bits -= code_len;
                        if (counter & 256)
                            break;

#if !TINFL_USE_64BIT_BITBUF
                        if (num_bits < 15)
                        {
                            bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);
                            pIn_buf_cur += 2;
                            num_bits += 16;
                        }
#endif
                        if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
                            code_len = sym2 >> 9;
                        else
                        {
                            code_len = TINFL_FAST_LOOKUP_BITS;
                            do
                            {
                                sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)];
                            } while (sym2 < 0);
                        }
                        bit_buf >>= code_len;
                        num_bits -= code_len;

                        pOut_buf_cur[0] = (mz_uint8)counter;
                        if (sym2 & 256)
                        {
                            pOut_buf_cur++;
                            counter = sym2;
                            break;
                        }
                        pOut_buf_cur[1] = (mz_uint8)sym2;
                        pOut_buf_cur += 2;
                    }
                }
                if ((counter &= 511) == 256)
                    break;

                num_extra = s_length_extra[counter - 257];
                counter = s_length_base[counter - 257];
                if (num_extra)
                {
                    mz_uint extra_bits;
                    TINFL_GET_BITS(25, extra_bits, num_extra);
                    counter += extra_bits;
                }

                TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1);
                num_extra = s_dist_extra[dist];
                dist = s_dist_base[dist];
                if (num_extra)
                {
                    mz_uint extra_bits;
                    TINFL_GET_BITS(27, extra_bits, num_extra);
                    dist += extra_bits;
                }

                dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
                if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
                {
                    TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
                }

                pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);

                if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
                {
                    while (counter--)
                    {
                        while (pOut_buf_cur >= pOut_buf_end)
                        {
                            TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT);
                        }
                        *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
                    }
                    continue;
                }
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
                else if ((counter >= 9) && (counter <= dist))
                {
                    const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
                    do
                    {
#ifdef MINIZ_UNALIGNED_USE_MEMCPY
						memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2);
#else
                        ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
                        ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
#endif
                        pOut_buf_cur += 8;
                    } while ((pSrc += 8) < pSrc_end);
                    if ((counter &= 7) < 3)
                    {
                        if (counter)
                        {
                            pOut_buf_cur[0] = pSrc[0];
                            if (counter > 1)
                                pOut_buf_cur[1] = pSrc[1];
                            pOut_buf_cur += counter;
                        }
                        continue;
                    }
                }
#endif
                while(counter>2)
                {
                    pOut_buf_cur[0] = pSrc[0];
                    pOut_buf_cur[1] = pSrc[1];
                    pOut_buf_cur[2] = pSrc[2];
                    pOut_buf_cur += 3;
                    pSrc += 3;
					counter -= 3;
                }
                if (counter > 0)
                {
                    pOut_buf_cur[0] = pSrc[0];
                    if (counter > 1)
                        pOut_buf_cur[1] = pSrc[1];
                    pOut_buf_cur += counter;
                }
            }
        }
    } while (!(r->m_final & 1));

    /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */
    /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */
    TINFL_SKIP_BITS(32, num_bits & 7);
    while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))
    {
        --pIn_buf_cur;
        num_bits -= 8;
    }
    bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits);
    MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */

    if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
    {
        for (counter = 0; counter < 4; ++counter)
        {
            mz_uint s;
            if (num_bits)
                TINFL_GET_BITS(41, s, 8);
            else
                TINFL_GET_BYTE(42, s);
            r->m_z_adler32 = (r->m_z_adler32 << 8) | s;
        }
    }
    TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);

    TINFL_CR_FINISH

common_exit:
    /* As long as we aren't telling the caller that we NEED more input to make forward progress: */
    /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */
    /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */
    if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS))
    {
        while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))
        {
            --pIn_buf_cur;
            num_bits -= 8;
        }
    }
    r->m_num_bits = num_bits;
    r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits);
    r->m_dist = dist;
    r->m_counter = counter;
    r->m_num_extra = num_extra;
    r->m_dist_from_out_buf_start = dist_from_out_buf_start;
    *pIn_buf_size = pIn_buf_cur - pIn_buf_next;
    *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
    if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
    {
        const mz_uint8 *ptr = pOut_buf_next;
        size_t buf_len = *pOut_buf_size;
        mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16;
        size_t block_len = buf_len % 5552;
        while (buf_len)
        {
            for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
            {
                s1 += ptr[0], s2 += s1;
                s1 += ptr[1], s2 += s1;
                s1 += ptr[2], s2 += s1;
                s1 += ptr[3], s2 += s1;
                s1 += ptr[4], s2 += s1;
                s1 += ptr[5], s2 += s1;
                s1 += ptr[6], s2 += s1;
                s1 += ptr[7], s2 += s1;
            }
            for (; i < block_len; ++i)
                s1 += *ptr++, s2 += s1;
            s1 %= 65521U, s2 %= 65521U;
            buf_len -= block_len;
            block_len = 5552;
        }
        r->m_check_adler32 = (s2 << 16) + s1;
        if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32))
            status = TINFL_STATUS_ADLER32_MISMATCH;
    }
    return status;
}

/* Higher level helper functions. */
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
    tinfl_decompressor decomp;
    void *pBuf = NULL, *pNew_buf;
    size_t src_buf_ofs = 0, out_buf_capacity = 0;
    *pOut_len = 0;
    tinfl_init(&decomp);
    for (;;)
    {
        size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
        tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size,
                                               (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
        if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
        {
            MZ_FREE(pBuf);
            *pOut_len = 0;
            return NULL;
        }
        src_buf_ofs += src_buf_size;
        *pOut_len += dst_buf_size;
        if (status == TINFL_STATUS_DONE)
            break;
        new_out_buf_capacity = out_buf_capacity * 2;
        if (new_out_buf_capacity < 128)
            new_out_buf_capacity = 128;
        pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
        if (!pNew_buf)
        {
            MZ_FREE(pBuf);
            *pOut_len = 0;
            return NULL;
        }
        pBuf = pNew_buf;
        out_buf_capacity = new_out_buf_capacity;
    }
    return pBuf;
}

size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
    tinfl_decompressor decomp;
    tinfl_status status;
    tinfl_init(&decomp);
    status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
    return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
}

int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
    int result = 0;
    tinfl_decompressor decomp;
    mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE);
    size_t in_buf_ofs = 0, dict_ofs = 0;
    if (!pDict)
        return TINFL_STATUS_FAILED;
    memset(pDict,0,TINFL_LZ_DICT_SIZE);
    tinfl_init(&decomp);
    for (;;)
    {
        size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
        tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
                                               (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
        in_buf_ofs += in_buf_size;
        if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
            break;
        if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
        {
            result = (status == TINFL_STATUS_DONE);
            break;
        }
        dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
    }
    MZ_FREE(pDict);
    *pIn_buf_size = in_buf_ofs;
    return result;
}

#ifndef MINIZ_NO_MALLOC
tinfl_decompressor *tinfl_decompressor_alloc(void)
{
    tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor));
    if (pDecomp)
        tinfl_init(pDecomp);
    return pDecomp;
}

void tinfl_decompressor_free(tinfl_decompressor *pDecomp)
{
    MZ_FREE(pDecomp);
}
#endif

#ifdef __cplusplus
}
#endif

#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/

repo_name: orochi/nblood

license: mit


code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "build.h"
#include "compat.h"
#include "baselayer.h"
#include "renderlayer.h"
#include "mmulti.h"

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <unistd.h>
#include <netinet/in.h>
#ifndef __BEOS__
#include <arpa/inet.h>
#endif
#ifdef __sun
#include <sys/filio.h>
#endif
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netdb.h>
#define SOCKET int
#define INVALID_HANDLE_VALUE (-1)
#define INVALID_SOCKET (-1)
#define SOCKET_ERROR (-1)
#define closesocket close
#define ioctlsocket ioctl
#define LPHOSTENT struct hostent *

#include <sys/time.h>
static int GetTickCount(void)
{
    struct timeval tv;
    int ti;
    if (gettimeofday(&tv,NULL) < 0) return 0;
    // tv is sec.usec, GTC gives msec
    ti = tv.tv_sec * 1000;
    ti += tv.tv_usec / 1000;
    return ti;
}
#endif

#ifdef KSFORBUILD
# include "compat.h"
# include "baselayer.h"
# define printf initprintf
#endif

#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif

#define MAXPLAYERS 16
#define MAXPAKSIZ 576

#define PAKRATE 250  //Packet rate/sec limit ... necessary?
int packetrate = PAKRATE;
#define SIMMIS 0     //Release:0  Test:100 Packets per 256 missed.
#define SIMLAG 0     //Release:0  Test: 10 Packets to delay receipt
#if (SIMLAG != 0)
static int simlagcnt[MAXPLAYERS];
static char simlagfif[MAXPLAYERS][SIMLAG+1][MAXPAKSIZ+2];
#endif
#if ((SIMMIS != 0) || (SIMLAG != 0))
#pragma message("\n\nWARNING! INTENTIONAL PACKET LOSS SIMULATION IS ENABLED!\nREMEMBER TO CHANGE SIMMIS&SIMLAG to 0 before RELEASE!\n\n")
#endif

int networkmode = -1;
int myconnectindex, numplayers;
int connecthead, connectpoint2[MAXPLAYERS];

static int tims, lastsendtims[MAXPLAYERS];
static char pakbuf[MAXPAKSIZ];

#define FIFSIZ 512 //16384/40 = 6min:49sec
static int ipak[MAXPLAYERS][FIFSIZ], icnt0[MAXPLAYERS];
static int opak[MAXPLAYERS][FIFSIZ], ocnt0[MAXPLAYERS], ocnt1[MAXPLAYERS];

#define PAKMEMSIZ (4096 * 1024) // WHY??????
static char *pakmem;
static int pakmemi = 1;

#define NETPORT 0x5bd9
static SOCKET mysock;
static int myip, myport = NETPORT, otherip[MAXPLAYERS], otherport[MAXPLAYERS];
static int snatchip = 0, snatchport = 0, netready = 0;

#ifdef _WIN32
int wsainitialized = 0;
#endif

/*Addfaz NatFree Start*/
int natfree; //NatFree mode flag
int nfCurrentPlayer = 0; //Current NatFree player counter. Will only talk with one player at a time
int nfFinished = 0; //Flag that determines NatFree has found all players and set the correct port numbers [normal routines can then take place]
int HeardFrom[MAXPLAYERS]; //For connecthead
int HeardFrom2[MAXPLAYERS]; //For others

int nfCheckCP(int other)  //Check if target player is our current NatFree Player
{
    if (!natfree || nfFinished)
        return 1;
    else
        if (nfCurrentPlayer == other) return 1;

    return 0;
}

int nfCheckHF(int other)  //function to check if we've heard from a player
{
    if (HeardFrom[other] == 1)
        return 1;
    else
        return 0;
}

void nfIncCP()  //function to handle currentplayer increment
{
    if (natfree && !nfFinished)
    {
        nfCurrentPlayer++; //Increment player counter
        if (nfCurrentPlayer == myconnectindex) nfCurrentPlayer++; //Bypass my index
        if (nfCurrentPlayer >= numplayers)
        {
            nfFinished = 1; //Set NatFree finished flag. Perform non-natfree networking routines
            initprintf("natfree: all players accounted for.\n");
            return;
        }
    }
}
/*Addfaz NatFree End*/

void netuninit()
{
    if (mysock != (SOCKET)INVALID_HANDLE_VALUE) closesocket(mysock);
#ifdef _WIN32
    if (wsainitialized)
        WSACleanup();
#endif
}

static int set_socket_blockmode(SOCKET socket, int onOrOff)
{
#ifdef _WIN32
    u_long flags;
#else
    signed int flags;
#endif
    int rc = 0;

    /* set socket to be (non-)blocking. */

#ifdef _WIN32
    flags = (onOrOff) ? 0 : 1;
    rc = (ioctlsocket(socket, FIONBIO, &flags) == 0);
#else
    flags = fcntl(socket, F_GETFL, 0);
    if (flags != -1)
    {
        if (onOrOff)
            flags &= ~O_NONBLOCK;
        else
            flags |= O_NONBLOCK;
        rc = (fcntl(socket, F_SETFL, flags) == 0);
    }
#endif

    return(rc);
}

int netinit(int portnum)
{
    LPHOSTENT lpHostEnt;
    char hostnam[256];
    struct sockaddr_in ip;
#ifdef __BEOS__
    int i;
#endif

#ifdef _WIN32
    if (wsainitialized == 0)
    {
        WSADATA ws;

        if (WSAStartup(0x101,&ws) == SOCKET_ERROR) return(0);
        wsainitialized = 1;
    }
#endif

    mysock = socket(AF_INET,SOCK_DGRAM,0); if (mysock == INVALID_SOCKET) return(0);
#ifdef __BEOS__
    i = 1; if (setsockopt(mysock,SOL_SOCKET,SO_NONBLOCK,&i,sizeof(i)) < 0) return(0);
#else
//    i = 1; if (ioctlsocket(mysock,FIONBIO,(unsigned int *)&i) == SOCKET_ERROR) return(0);
    if (!set_socket_blockmode(mysock,0)) return(0);
#endif

    ip.sin_family = AF_INET;
    ip.sin_addr.s_addr = INADDR_ANY;
    ip.sin_port = htons(portnum);
    if (bind(mysock,(struct sockaddr *)&ip,sizeof(ip)) != SOCKET_ERROR)
    {
        myport = portnum;
        if (gethostname(hostnam,sizeof(hostnam)) != SOCKET_ERROR)
            if ((lpHostEnt = gethostbyname(hostnam)))
            {
                myip = ip.sin_addr.s_addr = *(int *)lpHostEnt->h_addr;
                printf("mmulti: This machine's IP is %s\n", inet_ntoa(ip.sin_addr));
            }
        return(1);
    }
    return(0);
}

int netsend(int other, char *dabuf, int bufsiz)  //0:buffer full... can't send
{
    struct sockaddr_in ip;

    if (!otherip[other]) return(0);

    /*Addfaz NatFree Start*/
    if (natfree && !nfFinished)
    {
        if (other == connecthead && !nfCheckHF(connecthead))
            return(0); //Only greet the connecthead if we've heard from them.

        if (myconnectindex != connecthead)
        {
            if (!nfCheckCP(other) && other != connecthead)
                return(0); //Only connect to currentplayer or connecthead
            else if (!nfCheckCP(other) && !nfCheckHF(other))
                return(0);
        }
    }
    /*Addfaz NatFree End*/

    ip.sin_family = AF_INET;
    ip.sin_addr.s_addr = otherip[other];
    ip.sin_port = otherport[other];
    return(sendto(mysock,dabuf,bufsiz,0,(struct sockaddr *)&ip,sizeof(struct sockaddr_in)) != SOCKET_ERROR);
}

int netread(int *other, char *dabuf, int bufsiz)  //0:no packets in buffer
{
    struct sockaddr_in ip;
    int i;

    i = sizeof(ip);

    if (recvfrom(mysock,dabuf,bufsiz,0,(struct sockaddr *)&ip,(socklen_t *)&i) == -1) return(0);
#if (SIMMIS > 0)
    if ((wrand()&255) < SIMMIS) return(0);
#endif

    snatchip = (int)ip.sin_addr.s_addr; snatchport = (int)ip.sin_port;

    (*other) = myconnectindex;
    for (i=0;i<MAXPLAYERS;i++)
        if ((otherip[i] == snatchip) && (otherport[i] == snatchport))
            {(*other) = i; break; }

    /*Addfaz NatFree Start*/
    if (natfree && !nfFinished)
    {
        if (!nfCheckHF((*other))) //Check if have heard from this player already
        {
            if (otherip[nfCurrentPlayer] == snatchip) //Check IP Matches
            {
                if (!nfCheckHF(nfCurrentPlayer) && !HeardFrom2[nfCurrentPlayer])
                {
                    if (otherport[nfCurrentPlayer] != snatchport) //If Port numbers do not match
                    {
                        initprintf("natfree: port number for player %d changed from %d to %d.\n",nfCurrentPlayer,otherport[nfCurrentPlayer],snatchport);
                        otherport[nfCurrentPlayer] = snatchport; //Correct the port number
                    }
                }
                (*other) = nfCurrentPlayer; //Set pointer
                if (!nfCheckHF(nfCurrentPlayer) && myconnectindex != connecthead)
                {
                    HeardFrom[nfCurrentPlayer] = 1;
                }
            }
        }
        else
        {
            HeardFrom[i] = 1;
            HeardFrom2[i] = 1;
        }
    }
    /*Addfaz NatFree Endt*/

#if (SIMLAG > 1)
    i = simlagcnt[*other]%(SIMLAG+1);
    *(short *)&simlagfif[*other][i][0] = bufsiz; memcpy(&simlagfif[*other][i][2],dabuf,bufsiz);
    simlagcnt[*other]++; if (simlagcnt[*other] < SIMLAG+1) return(0);
    i = simlagcnt[*other]%(SIMLAG+1);
    bufsiz = *(short *)&simlagfif[*other][i][0]; memcpy(dabuf,&simlagfif[*other][i][2],bufsiz);
#endif

    return(1);
}

int isvalidipaddress(const char *st)
{
    int i, bcnt, num;

    bcnt = 0; num = 0;
    for (i=0;st[i];i++)
    {
        if (st[i] == '.') { bcnt++; num = 0; continue; }
        if (st[i] == ':')
        {
            if (bcnt != 3) return(0);
            num = 0;
            for (i++;st[i];i++)
            {
                if ((st[i] >= '0') && (st[i] <= '9'))
                    { num = num*10+st[i]-'0'; if (num >= 65536) return(0); }
                else return(0);
            }
            return(1);
        }
        if ((st[i] >= '0') && (st[i] <= '9'))
            { num = num*10+st[i]-'0'; if (num >= 256) return(0); }

    }
    return(bcnt == 3);
}

//---------------------------------- Obsolete variables&functions ----------------------------------
char syncstate = 0;
void mmulti_setpackettimeout(int datimeoutcount, int daresendagaincount) { UNREFERENCED_PARAMETER(datimeoutcount); UNREFERENCED_PARAMETER(daresendagaincount); }
void mmulti_generic(int other, unsigned char *bufptr, int messleng, int command)
{
    UNREFERENCED_PARAMETER(other);
    UNREFERENCED_PARAMETER(bufptr);
    UNREFERENCED_PARAMETER(messleng);
    UNREFERENCED_PARAMETER(command);
}
int mmulti_getoutputcirclesize() { return(0); }
void mmulti_flushpackets() {}
void mmulti_sendlogon() {}
void mmulti_sendlogoff() {}
//--------------------------------------------------------------------------------------------------

void mmulti_uninitmultiplayers()
{
    netuninit();
    DO_FREE_AND_NULL(pakmem);
}

static void initmultiplayers_reset(void)
{
    int i;

    initcrc16();
    memset(icnt0,0,sizeof(icnt0));
    memset(ocnt0,0,sizeof(ocnt0));
    memset(ocnt1,0,sizeof(ocnt1));
    memset(ipak,0,sizeof(ipak));
    //memset(opak,0,sizeof(opak)); //Don't need to init opak
    //memset(pakmem,0,sizeof(pakmem)); //Don't need to init pakmem
#if (SIMLAG > 1)
    memset(simlagcnt,0,sizeof(simlagcnt));
#endif

    lastsendtims[0] = GetTickCount();
    for (i=1;i<MAXPLAYERS;i++) lastsendtims[i] = lastsendtims[0];
    numplayers = 1; myconnectindex = 0;

    memset(otherip,0,sizeof(otherip));
    for (i=0;i<MAXPLAYERS;i++) otherport[i] = htons(NETPORT);
}

// Multiplayer command line summary. Assume myconnectindex always = 0 for 192.168.1.2
//
// /n0 (mast/slav) 2 player:               3 player:
// 192.168.1.2     game /n0                game /n0:3
// 192.168.1.100   game /n0 192.168.1.2    game /n0 192.168.1.2
// 192.168.1.4                             game /n0 192.168.1.2
//
// /n1 (peer-peer) 2 player:               3 player:
// 192.168.1.2     game /n1 192.168.1.100  game /n1 192.168.1.100 192.168.1.4
// 192.168.1.100   game 192.168.1.2 /n1    game 192.168.1.2 /n1 192.168.1.4
// 192.168.1.4                             game 192.168.1.2 192.168.1.100 /n1
int initmultiplayersparms(int argc, const char * const *argv)
{
    int i, j, daindex, portnum = NETPORT;
    char *st;

    pakmem = (char *)Xmalloc(PAKMEMSIZ);
    initmultiplayers_reset();
    networkmode = -1; daindex = 0;

//    if (!argv) return 0;
    // go looking for the port, if specified
    for (i=0;i<argc;i++)
    {
        if (argv[i][0] != '-' && argv[i][0] != '/') continue;
        if ((argv[i][1] == 'p' || argv[i][1] == 'P') && argv[i][2])
        {
            char *p;
            j = strtol(argv[i]+2, &p, 10);
            if (!(*p) && j > 1024 && j<65535) portnum = j;

            printf("mmulti: Using port %d\n", portnum);
        }
    }

    netinit(portnum);

    for (i=0;i<argc;i++)
    {
        //if (((argv[i][0] == '/') || (argv[i][0] == '-')) &&
        //    ((argv[i][1] == 'N') || (argv[i][1] == 'n')) &&
        //    ((argv[i][2] == 'E') || (argv[i][2] == 'e')) &&
        //    ((argv[i][3] == 'T') || (argv[i][3] == 't')) &&
        //     (!argv[i][4]))
        //   { foundnet = 1; continue; }
        //if (!foundnet) continue;

        if ((argv[i][0] == '-') || (argv[i][0] == '/'))
        {
            if ((argv[i][1] == 'N') || (argv[i][1] == 'n') || (argv[i][1] == 'I') || (argv[i][1] == 'i'))
            {
                numplayers = 2;
                if (argv[i][2] == '0')
                {
                    networkmode = MMULTI_MODE_MS;
                    if ((argv[i][3] == ':') && (argv[i][4] >= '0') && (argv[i][4] <= '9'))
                    {
                        numplayers = (argv[i][4]-'0');
                        if ((argv[i][5] >= '0') && (argv[i][5] <= '9')) numplayers = numplayers*10+(argv[i][5]-'0');
                        printf("mmulti: %d-player game\n", numplayers);
                    }
                    printf("mmulti: Master-slave mode\n");
                }
                else if (argv[i][2] == '1')
                {
                    networkmode = MMULTI_MODE_P2P;
                    myconnectindex = daindex; daindex++;
                    printf("mmulti: Peer-to-peer mode\n");
                }
                continue;
            }
            else if ((argv[i][1] == 'P') || (argv[i][1] == 'p')) continue;
        }

        st = strdup(argv[i]); if (!st) break;
        if (isvalidipaddress(st))
        {
            if ((networkmode == MMULTI_MODE_P2P) && (daindex == myconnectindex)) daindex++;
            for (j=0;st[j];j++)
            {
                if (st[j] == ':')
                    { otherport[daindex] = htons((unsigned short)atol(&st[j+1])); st[j] = 0; break; }
            }
            otherip[daindex] = inet_addr(st);
            printf("mmulti: Player %d at %s:%d\n",daindex,st,ntohs(otherport[daindex]));
            daindex++;
        }
        else
        {
            LPHOSTENT lph;
            unsigned short pt = htons(NETPORT);

            for (j=0;st[j];j++)
                if (st[j] == ':')
                    { pt = htons((unsigned short)atol(&st[j+1])); st[j] = 0; break; }
            if ((lph = gethostbyname(st)))
            {
                if ((networkmode == MMULTI_MODE_P2P) && (daindex == myconnectindex)) daindex++;
                otherip[daindex] = *(int *)lph->h_addr;
                otherport[daindex] = pt;
                printf("mmulti: Player %d at %s:%d (%s)\n",daindex,
                       inet_ntoa(*(struct in_addr *)lph->h_addr),ntohs(pt),argv[i]);
                daindex++;
            }
            else printf("mmulti: Failed resolving %s\n",argv[i]);
        }
        free(st);
    }
    if ((networkmode == -1) && (daindex)) { numplayers = 2; networkmode = MMULTI_MODE_MS; } //an IP w/o /n# defaults to /n0
    if ((numplayers >= 2) && (daindex) && (networkmode == MMULTI_MODE_MS)) myconnectindex = 1;
    if (daindex > numplayers) numplayers = daindex;

    //for(i=0;i<numplayers;i++)
    //   printf("Player %d: %d.%d.%d.%d:%d\n",i,otherip[i]&255,(otherip[i]>>8)&255,(otherip[i]>>16)&255,((unsigned int)otherip[i])>>24,ntohs(otherport[i]));

    connecthead = 0;
    for (i=0;i<numplayers-1;i++) connectpoint2[i] = i+1;
    connectpoint2[numplayers-1] = -1;

    return (numplayers >= 2);
}

int initmultiplayerscycle(void)
{
    int i, k;
//    extern int totalclock;

    handleevents();
    idle();

    mmulti_getpacket(&i,0);

    tims = GetTickCount();
    if (myconnectindex == connecthead) //Player 0 only
    {
        for (i=numplayers-1;i>0;i--)
            if (!otherip[i]) break;
        if (!i)
        {
            nfIncCP(); //Addfaz NatFree
            netready = 1; //Player 0 is ready
            return 0;
        }
    }
    else
    {
        if (netready) return 0;
        if (tims < lastsendtims[connecthead]) lastsendtims[connecthead] = tims;
        if (tims >= lastsendtims[connecthead]+250) //1000/PAKRATE)
        {
            lastsendtims[connecthead] = tims;

            //   short crc16ofs;       //offset of crc16
            //   int icnt0;           //-1 (special packet for MMULTI.C's player collection)
            //   ...
            //   unsigned short crc16; //CRC16 of everything except crc16
            k = 2;
            *(int *)&pakbuf[k] = -1; k += 4;
            pakbuf[k++] = 0xaa;
            *(unsigned short *)&pakbuf[0] = (unsigned short)k;
            *(unsigned short *)&pakbuf[k] = getcrc16(pakbuf,k); k += 2;
            netsend(connecthead,pakbuf,k);
        }
    }

    return 1;
}

void mmulti_initmultiplayers(int argc, const char * const *argv, char damultioption, char dacomrateoption, char dapriority)
{
    UNREFERENCED_PARAMETER(damultioption);
    UNREFERENCED_PARAMETER(dacomrateoption);
    UNREFERENCED_PARAMETER(dapriority);

    if (initmultiplayersparms(argc,argv))
    {
#if 0
        int i, j, k, otims;
        //Console code seems to crash Win98 upon quitting game
        //it's not necessary and it's not portable anyway
        char tbuf[1024];
        unsigned int u;
        HANDLE hconsout;
        AllocConsole();
        SetConsoleTitle("Multiplayer status...");
        hconsout = GetStdHandle(STD_OUTPUT_HANDLE);
        otims = 0;
#endif
        while (initmultiplayerscycle())
        {
#if 0
            if ((tims < otims) || (tims > otims+100))
            {
                otims = tims;
                sprintf(tbuf,"\rWait for players (%d/%d): ",myconnectindex,numplayers);
                for (i=0;i<numplayers;i++)
                {
                    if (i == myconnectindex) { strcat(tbuf,"<me> "); continue; }
                    if (!otherip[i]) { strcat(tbuf,"?.?.?.?:? "); continue; }
                    sprintf(&tbuf[strlen(tbuf)],"%d.%d.%d.%d:%04x ",otherip[i]&255,(otherip[i]>>8)&255,(otherip[i]>>16)&255,(((unsigned int)otherip[i])>>24),otherport[i]);
                }
                WriteConsole(hconsout,tbuf,strlen(tbuf),&u,0);
            }
        }
        FreeConsole();
#else
        }
#endif
    }
    netready = 1;
}

void dosendpackets(int other)  //Host to send intially, client to send to others once heard from host.
{
    int i, j, k;

    if (!otherip[other]) return;

    //Packet format:
    //   short crc16ofs;       //offset of crc16
    //   int icnt0;           //earliest unacked packet
    //   char ibits[32];       //ack status of packets icnt0<=i<icnt0+256
    //   while (short leng)    //leng: !=0 for packet, 0 for no more packets
    //   {
    //      int ocnt;         //index of following packet data
    //      char pak[leng];    //actual packet data :)
    //   }
    //   unsigned short crc16; //CRC16 of everything except crc16

    tims = GetTickCount();
    if (tims < lastsendtims[other]) lastsendtims[other] = tims;
    if (tims < lastsendtims[other]+1000/packetrate) return;
    lastsendtims[other] = tims;

    k = 2;
    *(int *)&pakbuf[k] = icnt0[other]; k += 4;
    memset(&pakbuf[k],0,32);
    for (i=icnt0[other];i<icnt0[other]+256;i++)
        if (ipak[other][i&(FIFSIZ-1)])
            pakbuf[((i-icnt0[other])>>3)+k] |= (1<<((i-icnt0[other])&7));
    k += 32;

    while ((ocnt0[other] < ocnt1[other]) && (!opak[other][ocnt0[other]&(FIFSIZ-1)])) ocnt0[other]++;
    for (i=ocnt0[other];i<ocnt1[other];i++)
    {
        j = *(short *)&pakmem[opak[other][i&(FIFSIZ-1)]]; if (!j) continue; //packet already acked
        if (k+6+j+4 > (int)sizeof(pakbuf)) break;

        *(unsigned short *)&pakbuf[k] = (unsigned short)j; k += 2;
        *(int *)&pakbuf[k] = i; k += 4;
        memcpy(&pakbuf[k],&pakmem[opak[other][i&(FIFSIZ-1)]+2],j); k += j;
    }
    *(unsigned short *)&pakbuf[k] = 0; k += 2;
    *(unsigned short *)&pakbuf[0] = (unsigned short)k;
    *(unsigned short *)&pakbuf[k] = getcrc16(pakbuf,k); k += 2;

    //printf("Send: "); for(i=0;i<k;i++) printf("%02x ",pakbuf[i]); printf("\n");

    /*Addfaz NatFree Start*/
    if (nfCheckCP(other) || !natfree) //Clients only if heard from host
        netsend(other,pakbuf,k);
    /*Addfaz NatFree End*/
}

void mmulti_sendpacket(int other, unsigned char *bufptr, int messleng)
{
//    int i, j;

    if (numplayers < 2) return;

    // WHY???????????????
    if (pakmemi+messleng+2 > PAKMEMSIZ) pakmemi = 1;
    opak[other][ocnt1[other]&(FIFSIZ-1)] = pakmemi;
    *(short *)&pakmem[pakmemi] = messleng;
    memcpy(&pakmem[pakmemi+2],bufptr,messleng); pakmemi += messleng+2;
    ocnt1[other]++;

    //printf("Send: "); for(i=0;i<messleng;i++) printf("%02x ",bufptr[i]); printf("\n");

    dosendpackets(other);
}

//passing bufptr == 0 enables receive&sending raw packets but does not return any received packets
//(used as hack for player collection)
int mmulti_getpacket(int *retother, unsigned char *bufptr)
{
    int i, j, k, ic0, crc16ofs, messleng, other;

    if (numplayers < 2) return(0);

    if (netready)
    {
        for (i=connecthead;i>=0;i=connectpoint2[i])
        {
            if (i != myconnectindex) dosendpackets(i);
            if ((networkmode == MMULTI_MODE_MS) && (myconnectindex != connecthead)) break; //slaves in M/S mode only send to master
        }
    }

    while (netread(&other,pakbuf,sizeof(pakbuf)))
    {
        //Packet format:
        //   short crc16ofs;       //offset of crc16
        //   int icnt0;           //earliest unacked packet
        //   char ibits[32];       //ack status of packets icnt0<=i<icnt0+256
        //   while (short leng)    //leng: !=0 for packet, 0 for no more packets
        //   {
        //      int ocnt;         //index of following packet data
        //      char pak[leng];    //actual packet data :)
        //   }
        //   unsigned short crc16; //CRC16 of everything except crc16
        k = 0;
        crc16ofs = (int)(*(unsigned short *)&pakbuf[k]); k += 2;

        //printf("Recv: "); for(i=0;i<crc16ofs+2;i++) printf("%02x ",pakbuf[i]); printf("\n");

        if ((crc16ofs+2 <= (int)sizeof(pakbuf)) && (getcrc16(pakbuf,crc16ofs) == (*(unsigned short *)&pakbuf[crc16ofs])))
        {
            ic0 = *(int *)&pakbuf[k]; k += 4;
            if (ic0 == -1)
            {
                //Slave sends 0xaa to Master at mmulti_initmultiplayers() and waits for 0xab response
                //Master responds to slave with 0xab whenever it receives a 0xaa - even if during game!
                if ((pakbuf[k] == 0xaa) && (myconnectindex == connecthead))
                {
                    for (other=1;other<numplayers;other++)
                    {
                        //Only send to others asking for a response
                        if ((otherip[other]) && ((otherip[other] != snatchip) || (otherport[other] != snatchport))) continue;
                        otherip[other] = snatchip;
                        otherport[other] = snatchport;

                        //   short crc16ofs;       //offset of crc16
                        //   int icnt0;           //-1 (special packet for MMULTI.C's player collection)
                        //   ...
                        //   unsigned short crc16; //CRC16 of everything except crc16
                        k = 2;
                        *(int *)&pakbuf[k] = -1; k += 4;
                        pakbuf[k++] = 0xab;
                        pakbuf[k++] = (char)other;
                        pakbuf[k++] = (char)numplayers;
                        *(unsigned short *)&pakbuf[0] = (unsigned short)k;
                        *(unsigned short *)&pakbuf[k] = getcrc16(pakbuf,k); k += 2;

                        /*Addfaz NatFree Start*/
                        if (natfree && !nfCheckHF(other))
                        {
                            initprintf("natfree: heard from player %d\n", other);
                            HeardFrom[other] = 1;
                            HeardFrom2[other] = 1;
                            nfIncCP();
                        }
                        else
                        {
                            initprintf("mmulti: player %d connected! (%d.%d.%d.%d:%d)\n", other, otherip[other]&255,(otherip[other]>>8)&255,(otherip[other]>>16)&255,(((unsigned int)otherip[other])>>24),otherport[other]);
                        }
                        /*Addfaz NatFree End*/

                        netsend(other,pakbuf,k);
                        break;
                    }
                }
                else if ((pakbuf[k] == 0xab) && (myconnectindex != connecthead))
                {
                    if (((unsigned int)pakbuf[k+1] < (unsigned int)pakbuf[k+2]) &&
                            ((unsigned int)pakbuf[k+2] < (unsigned int)MAXPLAYERS))
                    {
                        myconnectindex = (int)pakbuf[k+1];
                        numplayers = (int)pakbuf[k+2];

                        connecthead = 0;
                        for (i=0;i<numplayers-1;i++) connectpoint2[i] = i+1;
                        connectpoint2[numplayers-1] = -1;

                        otherip[connecthead] = snatchip;
                        otherport[connecthead] = snatchport;
                        netready = 1;

                        /*Addfaz NatFree Start*/
                        if (natfree)
                        {
                            initprintf("natfree: heard from player %d (head).\n", connecthead);
                            HeardFrom[connecthead] = 1;
                            HeardFrom2[connecthead] = 1;
                            nfIncCP();
                        }
                        /*Addfaz NatFree End*/
                    }
                }
            }
            else
            {
                if (ocnt0[other] < ic0) ocnt0[other] = ic0;
                for (i=ic0;i<min(ic0+256,ocnt1[other]);i++)
                    if (pakbuf[((i-ic0)>>3)+k]&(1<<((i-ic0)&7)))
                        opak[other][i&(FIFSIZ-1)] = 0;
                k += 32;

                messleng = (int)(*(unsigned short *)&pakbuf[k]); k += 2;
                while (messleng)
                {
                    j = *(int *)&pakbuf[k]; k += 4;
                    if ((j >= icnt0[other]) && (!ipak[other][j&(FIFSIZ-1)]))
                    {
                        if (pakmemi+messleng+2 > PAKMEMSIZ) pakmemi = 1;
                        ipak[other][j&(FIFSIZ-1)] = pakmemi;
                        *(short *)&pakmem[pakmemi] = messleng;
                        memcpy(&pakmem[pakmemi+2],&pakbuf[k],messleng); pakmemi += messleng+2;
                    }
                    k += messleng;
                    messleng = (int)(*(unsigned short *)&pakbuf[k]); k += 2;
                }
            }
        }
    }

    //Return next valid packet from any player
    if (!bufptr) return(0);
    for (i=connecthead;i>=0;i=connectpoint2[i])
    {
        if (i != myconnectindex)
        {
            j = ipak[i][icnt0[i]&(FIFSIZ-1)];
            if (j)
            {
                messleng = *(short *)&pakmem[j]; memcpy(bufptr,&pakmem[j+2],messleng);
                *retother = i; ipak[i][icnt0[i]&(FIFSIZ-1)] = 0; icnt0[i]++;
                //printf("Recv: "); for(i=0;i<messleng;i++) printf("%02x ",bufptr[i]); printf("\n");

                /*Addfaz NatFree Start*/
                if (natfree && !HeardFrom2[nfCurrentPlayer] && i == nfCurrentPlayer)
                {
                    initprintf("natfree: heard from player %d.\n", i);
                    HeardFrom2[nfCurrentPlayer] = 1;
                    HeardFrom[nfCurrentPlayer] = 1;
                    nfIncCP();
                }
                /*Addfaz NatFree End*/

                return(messleng);
            }
        }
        if ((networkmode == MMULTI_MODE_MS) && (myconnectindex != connecthead)) break; //slaves in M/S mode only send to master
    }

    return(0);
}

int getexternaladdress(char *buffer, const char *host, int port)
{
    int bytes_sent, i=0, j=0;
    struct sockaddr_in dest_addr;
    struct hostent *h;
    const char *req = "GET / HTTP/1.0\r\n\r\n";
    const char *text = "Current IP Address: ";
    char tempbuf[1024], ipaddr[32];

    memset(buffer, 0, sizeof(ipaddr));

#ifdef _WIN32
    if (wsainitialized == 0)
    {
        WSADATA ws;

        if (WSAStartup(0x101,&ws) == SOCKET_ERROR)
        {
            initprintf("mmulti: Winsock error in getexternaladdress() (%d)\n",errno);
            return(0);
        }
        wsainitialized = 1;
    }
#endif

    if ((h=gethostbyname(host)) == NULL)
    {
        initprintf("mmulti: gethostbyname() error in getexternaladdress() (%d)\n",h_errno);
        return(0);
    }

    dest_addr.sin_addr.s_addr = ((struct in_addr *)(h->h_addr))->s_addr;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(port);

    memset(&(dest_addr.sin_zero), '\0', 8);

    mysock = socket(PF_INET, SOCK_STREAM, 0);

    if (mysock == INVALID_SOCKET)
    {
        initprintf("mmulti: socket() error in getexternaladdress() (%d)\n",errno);
        return(0);
    }

    if (connect(mysock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)) == SOCKET_ERROR)
    {
        initprintf("mmulti: connect() error in getexternaladdress() (%d)\n",errno);
        return(0);
    }

    bytes_sent = send(mysock, req, strlen(req), 0);
    if (bytes_sent == SOCKET_ERROR)
    {
        initprintf("mmulti: send() error in getexternaladdress() (%d)\n",errno);
        return(0);
    }

    //    initprintf("sent %d bytes\n",bytes_sent);
    recv(mysock, (char *)&tempbuf, sizeof(tempbuf), 0);
    closesocket(mysock);
    j = Bstrlen(text);
    for (i=Bstrlen(tempbuf);i>0;i--)
        if (!Bstrncmp(&tempbuf[i], text, j))
        {
            i += j;
            j = 0;
            while (isdigit(tempbuf[i]) || (tempbuf[i] == '.'))
            {
                ipaddr[j] = tempbuf[i];
                i++, j++;
            }
            ipaddr[j++] = '\0';
            break;
        }
    Bmemcpy(buffer,&ipaddr,j);
    return(1);
}


code:
#include "compat.h"
#include "mutex.h"

int32_t mutex_init(mutex_t *mutex)
{
#if SDL_MAJOR_VERSION >= 2
    // SDL2's spinlock is most preferable
    *mutex = 0;
    return 0;
#elif defined _WIN32
    // Windows' "Critical Section" is preferable to "Mutex" -- within a single process only
    // https://preshing.com/20111124/always-use-a-lightweight-mutex/
    InitializeCriticalSection(mutex);
    return 0;
#elif SDL_MAJOR_VERSION == 1
    if (mutex)
    {
        *mutex = SDL_CreateMutex();
        if (*mutex != NULL)
            return 0;
    }
    return -1;
#else
    return -1;
#endif
}

void mutex_destroy(mutex_t *mutex)
{
#if SDL_MAJOR_VERSION >= 2
    *mutex = 0;
#elif defined _WIN32
    DeleteCriticalSection(mutex);
#elif SDL_MAJOR_VERSION == 1
    if (mutex)
    {
        SDL_DestroyMutex(*mutex);
        *mutex = nullptr;
    }
#endif
}