opening TIC-80 sources
This commit is contained in:
		
							
								
								
									
										346
									
								
								src/ext/blip_buf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								src/ext/blip_buf.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,346 @@
 | 
			
		||||
/* blip_buf $vers. http://www.slack.net/~ant/ */
 | 
			
		||||
 | 
			
		||||
#include "blip_buf.h"
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
/* Library Copyright (C) 2003-2009 Shay Green. This library is free software;
 | 
			
		||||
you can redistribute it and/or modify it under the terms of the GNU Lesser
 | 
			
		||||
General Public License as published by the Free Software Foundation; either
 | 
			
		||||
version 2.1 of the License, or (at your option) any later version. This
 | 
			
		||||
library is distributed in the hope that it will be useful, but WITHOUT ANY
 | 
			
		||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 | 
			
		||||
details. You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
License along with this module; if not, write to the Free Software Foundation,
 | 
			
		||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
 | 
			
		||||
 | 
			
		||||
#if defined (BLARGG_TEST) && BLARGG_TEST
 | 
			
		||||
	#include "blargg_test.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000.
 | 
			
		||||
Avoids constants that don't fit in 32 bits. */
 | 
			
		||||
#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF
 | 
			
		||||
	typedef unsigned long fixed_t;
 | 
			
		||||
	enum { pre_shift = 32 };
 | 
			
		||||
 | 
			
		||||
#elif defined(ULLONG_MAX)
 | 
			
		||||
	typedef unsigned long long fixed_t;
 | 
			
		||||
	enum { pre_shift = 32 };
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
	typedef unsigned fixed_t;
 | 
			
		||||
	enum { pre_shift = 0 };
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum { time_bits = pre_shift + 20 };
 | 
			
		||||
 | 
			
		||||
static fixed_t const time_unit = (fixed_t) 1 << time_bits;
 | 
			
		||||
 | 
			
		||||
enum { bass_shift  = 9 }; /* affects high-pass filter breakpoint frequency */
 | 
			
		||||
enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */
 | 
			
		||||
 | 
			
		||||
enum { half_width  = 8 };
 | 
			
		||||
enum { buf_extra   = half_width*2 + end_frame_extra };
 | 
			
		||||
enum { phase_bits  = 5 };
 | 
			
		||||
enum { phase_count = 1 << phase_bits };
 | 
			
		||||
enum { delta_bits  = 15 };
 | 
			
		||||
enum { delta_unit  = 1 << delta_bits };
 | 
			
		||||
enum { frac_bits = time_bits - pre_shift };
 | 
			
		||||
 | 
			
		||||
/* We could eliminate avail and encode whole samples in offset, but that would
 | 
			
		||||
limit the total buffered samples to blip_max_frame. That could only be
 | 
			
		||||
increased by decreasing time_bits, which would reduce resample ratio accuracy.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/** Sample buffer that resamples to output rate and accumulates samples
 | 
			
		||||
until they're read out */
 | 
			
		||||
struct blip_t
 | 
			
		||||
{
 | 
			
		||||
	fixed_t factor;
 | 
			
		||||
	fixed_t offset;
 | 
			
		||||
	int avail;
 | 
			
		||||
	int size;
 | 
			
		||||
	int integrator;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef int buf_t;
 | 
			
		||||
 | 
			
		||||
/* probably not totally portable */
 | 
			
		||||
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
 | 
			
		||||
 | 
			
		||||
/* Arithmetic (sign-preserving) right shift */
 | 
			
		||||
#define ARITH_SHIFT( n, shift ) \
 | 
			
		||||
	((n) >> (shift))
 | 
			
		||||
 | 
			
		||||
enum { max_sample = +32767 };
 | 
			
		||||
enum { min_sample = -32768 };
 | 
			
		||||
 | 
			
		||||
#define CLAMP( n ) \
 | 
			
		||||
	{\
 | 
			
		||||
		if ( (short) n != n )\
 | 
			
		||||
			n = ARITH_SHIFT( n, 16 ) ^ max_sample;\
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
static void check_assumptions( void )
 | 
			
		||||
{
 | 
			
		||||
	int n;
 | 
			
		||||
	
 | 
			
		||||
	#if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF
 | 
			
		||||
		#error "int must be at least 32 bits"
 | 
			
		||||
	#endif
 | 
			
		||||
	
 | 
			
		||||
	assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */
 | 
			
		||||
	
 | 
			
		||||
	n = max_sample * 2;
 | 
			
		||||
	CLAMP( n );
 | 
			
		||||
	assert( n == max_sample );
 | 
			
		||||
	
 | 
			
		||||
	n = min_sample * 2;
 | 
			
		||||
	CLAMP( n );
 | 
			
		||||
	assert( n == min_sample );
 | 
			
		||||
	
 | 
			
		||||
	assert( blip_max_ratio <= time_unit );
 | 
			
		||||
	assert( blip_max_frame <= (fixed_t) -1 >> time_bits );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
blip_t* blip_new( int size )
 | 
			
		||||
{
 | 
			
		||||
	blip_t* m;
 | 
			
		||||
	assert( size >= 0 );
 | 
			
		||||
	
 | 
			
		||||
	m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
 | 
			
		||||
	if ( m )
 | 
			
		||||
	{
 | 
			
		||||
		m->factor = time_unit / blip_max_ratio;
 | 
			
		||||
		m->size   = size;
 | 
			
		||||
		blip_clear( m );
 | 
			
		||||
		check_assumptions();
 | 
			
		||||
	}
 | 
			
		||||
	return m;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blip_delete( blip_t* m )
 | 
			
		||||
{
 | 
			
		||||
	if ( m != NULL )
 | 
			
		||||
	{
 | 
			
		||||
		/* Clear fields in case user tries to use after freeing */
 | 
			
		||||
		memset( m, 0, sizeof *m );
 | 
			
		||||
		free( m );
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
 | 
			
		||||
{
 | 
			
		||||
	double factor = time_unit * sample_rate / clock_rate;
 | 
			
		||||
	m->factor = (fixed_t) factor;
 | 
			
		||||
	
 | 
			
		||||
	/* Fails if clock_rate exceeds maximum, relative to sample_rate */
 | 
			
		||||
	assert( 0 <= factor - m->factor && factor - m->factor < 1 );
 | 
			
		||||
	
 | 
			
		||||
	/* Avoid requiring math.h. Equivalent to
 | 
			
		||||
	m->factor = (int) ceil( factor ) */
 | 
			
		||||
	if ( m->factor < factor )
 | 
			
		||||
		m->factor++;
 | 
			
		||||
	
 | 
			
		||||
	/* At this point, factor is most likely rounded up, but could still
 | 
			
		||||
	have been rounded down in the floating-point calculation. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blip_clear( blip_t* m )
 | 
			
		||||
{
 | 
			
		||||
	/* We could set offset to 0, factor/2, or factor-1. 0 is suitable if
 | 
			
		||||
	factor is rounded up. factor-1 is suitable if factor is rounded down.
 | 
			
		||||
	Since we don't know rounding direction, factor/2 accommodates either,
 | 
			
		||||
	with the slight loss of showing an error in half the time. Since for
 | 
			
		||||
	a 64-bit factor this is years, the halving isn't a problem. */
 | 
			
		||||
	
 | 
			
		||||
	m->offset     = m->factor / 2;
 | 
			
		||||
	m->avail      = 0;
 | 
			
		||||
	m->integrator = 0;
 | 
			
		||||
	memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blip_clocks_needed( const blip_t* m, int samples )
 | 
			
		||||
{
 | 
			
		||||
	fixed_t needed;
 | 
			
		||||
	
 | 
			
		||||
	/* Fails if buffer can't hold that many more samples */
 | 
			
		||||
	assert( samples >= 0 && m->avail + samples <= m->size );
 | 
			
		||||
	
 | 
			
		||||
	needed = (fixed_t) samples * time_unit;
 | 
			
		||||
	if ( needed < m->offset )
 | 
			
		||||
		return 0;
 | 
			
		||||
	
 | 
			
		||||
	return (int)((needed - m->offset + m->factor - 1) / m->factor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blip_end_frame( blip_t* m, unsigned t )
 | 
			
		||||
{
 | 
			
		||||
	fixed_t off = t * m->factor + m->offset;
 | 
			
		||||
	m->avail += off >> time_bits;
 | 
			
		||||
	m->offset = off & (time_unit - 1);
 | 
			
		||||
	
 | 
			
		||||
	/* Fails if buffer size was exceeded */
 | 
			
		||||
	assert( m->avail <= m->size );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blip_samples_avail( const blip_t* m )
 | 
			
		||||
{
 | 
			
		||||
	return m->avail;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void remove_samples( blip_t* m, int count )
 | 
			
		||||
{
 | 
			
		||||
	buf_t* buf = SAMPLES( m );
 | 
			
		||||
	int remain = m->avail + buf_extra - count;
 | 
			
		||||
	m->avail -= count;
 | 
			
		||||
	
 | 
			
		||||
	memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
 | 
			
		||||
	memset( &buf [remain], 0, count * sizeof buf [0] );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blip_read_samples( blip_t* m, short out [], int count)
 | 
			
		||||
{
 | 
			
		||||
	assert( count >= 0 );
 | 
			
		||||
	
 | 
			
		||||
	if ( count > m->avail )
 | 
			
		||||
		count = m->avail;
 | 
			
		||||
	
 | 
			
		||||
	if ( count )
 | 
			
		||||
	{
 | 
			
		||||
		int const step = 1;
 | 
			
		||||
		buf_t const* in  = SAMPLES( m );
 | 
			
		||||
		buf_t const* end = in + count;
 | 
			
		||||
		int sum = m->integrator;
 | 
			
		||||
		do
 | 
			
		||||
		{
 | 
			
		||||
			/* Eliminate fraction */
 | 
			
		||||
			int s = ARITH_SHIFT( sum, delta_bits );
 | 
			
		||||
			
 | 
			
		||||
			sum += *in++;
 | 
			
		||||
			
 | 
			
		||||
			CLAMP( s );
 | 
			
		||||
			
 | 
			
		||||
			int size = step;
 | 
			
		||||
			while(size--)
 | 
			
		||||
				*out++ = s;
 | 
			
		||||
			// out += step;
 | 
			
		||||
			
 | 
			
		||||
			/* High-pass filter */
 | 
			
		||||
			sum -= s << (delta_bits - bass_shift);
 | 
			
		||||
		}
 | 
			
		||||
		while ( in != end );
 | 
			
		||||
		m->integrator = sum;
 | 
			
		||||
		
 | 
			
		||||
		remove_samples( m, count );
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Things that didn't help performance on x86:
 | 
			
		||||
	__attribute__((aligned(128)))
 | 
			
		||||
	#define short int
 | 
			
		||||
	restrict
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* Sinc_Generator( 0.9, 0.55, 4.5 ) */
 | 
			
		||||
static short const bl_step [phase_count + 1] [half_width] =
 | 
			
		||||
{
 | 
			
		||||
{   43, -115,  350, -488, 1136, -914, 5861,21022},
 | 
			
		||||
{   44, -118,  348, -473, 1076, -799, 5274,21001},
 | 
			
		||||
{   45, -121,  344, -454, 1011, -677, 4706,20936},
 | 
			
		||||
{   46, -122,  336, -431,  942, -549, 4156,20829},
 | 
			
		||||
{   47, -123,  327, -404,  868, -418, 3629,20679},
 | 
			
		||||
{   47, -122,  316, -375,  792, -285, 3124,20488},
 | 
			
		||||
{   47, -120,  303, -344,  714, -151, 2644,20256},
 | 
			
		||||
{   46, -117,  289, -310,  634,  -17, 2188,19985},
 | 
			
		||||
{   46, -114,  273, -275,  553,  117, 1758,19675},
 | 
			
		||||
{   44, -108,  255, -237,  471,  247, 1356,19327},
 | 
			
		||||
{   43, -103,  237, -199,  390,  373,  981,18944},
 | 
			
		||||
{   42,  -98,  218, -160,  310,  495,  633,18527},
 | 
			
		||||
{   40,  -91,  198, -121,  231,  611,  314,18078},
 | 
			
		||||
{   38,  -84,  178,  -81,  153,  722,   22,17599},
 | 
			
		||||
{   36,  -76,  157,  -43,   80,  824, -241,17092},
 | 
			
		||||
{   34,  -68,  135,   -3,    8,  919, -476,16558},
 | 
			
		||||
{   32,  -61,  115,   34,  -60, 1006, -683,16001},
 | 
			
		||||
{   29,  -52,   94,   70, -123, 1083, -862,15422},
 | 
			
		||||
{   27,  -44,   73,  106, -184, 1152,-1015,14824},
 | 
			
		||||
{   25,  -36,   53,  139, -239, 1211,-1142,14210},
 | 
			
		||||
{   22,  -27,   34,  170, -290, 1261,-1244,13582},
 | 
			
		||||
{   20,  -20,   16,  199, -335, 1301,-1322,12942},
 | 
			
		||||
{   18,  -12,   -3,  226, -375, 1331,-1376,12293},
 | 
			
		||||
{   15,   -4,  -19,  250, -410, 1351,-1408,11638},
 | 
			
		||||
{   13,    3,  -35,  272, -439, 1361,-1419,10979},
 | 
			
		||||
{   11,    9,  -49,  292, -464, 1362,-1410,10319},
 | 
			
		||||
{    9,   16,  -63,  309, -483, 1354,-1383, 9660},
 | 
			
		||||
{    7,   22,  -75,  322, -496, 1337,-1339, 9005},
 | 
			
		||||
{    6,   26,  -85,  333, -504, 1312,-1280, 8355},
 | 
			
		||||
{    4,   31,  -94,  341, -507, 1278,-1205, 7713},
 | 
			
		||||
{    3,   35, -102,  347, -506, 1238,-1119, 7082},
 | 
			
		||||
{    1,   40, -110,  350, -499, 1190,-1021, 6464},
 | 
			
		||||
{    0,   43, -115,  350, -488, 1136, -914, 5861}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Shifting by pre_shift allows calculation using unsigned int rather than
 | 
			
		||||
possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
 | 
			
		||||
And by having pre_shift 32, a 32-bit platform can easily do the shift by
 | 
			
		||||
simply ignoring the low half. */
 | 
			
		||||
 | 
			
		||||
void blip_add_delta( blip_t* m, unsigned time, int delta )
 | 
			
		||||
{
 | 
			
		||||
	unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
 | 
			
		||||
	buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
 | 
			
		||||
	
 | 
			
		||||
	int const phase_shift = frac_bits - phase_bits;
 | 
			
		||||
	int phase = fixed >> phase_shift & (phase_count - 1);
 | 
			
		||||
	short const* in  = bl_step [phase];
 | 
			
		||||
	short const* rev = bl_step [phase_count - phase];
 | 
			
		||||
	
 | 
			
		||||
	int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
 | 
			
		||||
	int delta2 = (delta * interp) >> delta_bits;
 | 
			
		||||
	delta -= delta2;
 | 
			
		||||
	
 | 
			
		||||
	/* Fails if buffer size was exceeded */
 | 
			
		||||
	assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
 | 
			
		||||
	
 | 
			
		||||
	out [0] += in[0]*delta + in[half_width+0]*delta2;
 | 
			
		||||
	out [1] += in[1]*delta + in[half_width+1]*delta2;
 | 
			
		||||
	out [2] += in[2]*delta + in[half_width+2]*delta2;
 | 
			
		||||
	out [3] += in[3]*delta + in[half_width+3]*delta2;
 | 
			
		||||
	out [4] += in[4]*delta + in[half_width+4]*delta2;
 | 
			
		||||
	out [5] += in[5]*delta + in[half_width+5]*delta2;
 | 
			
		||||
	out [6] += in[6]*delta + in[half_width+6]*delta2;
 | 
			
		||||
	out [7] += in[7]*delta + in[half_width+7]*delta2;
 | 
			
		||||
	
 | 
			
		||||
	in = rev;
 | 
			
		||||
	out [ 8] += in[7]*delta + in[7-half_width]*delta2;
 | 
			
		||||
	out [ 9] += in[6]*delta + in[6-half_width]*delta2;
 | 
			
		||||
	out [10] += in[5]*delta + in[5-half_width]*delta2;
 | 
			
		||||
	out [11] += in[4]*delta + in[4-half_width]*delta2;
 | 
			
		||||
	out [12] += in[3]*delta + in[3-half_width]*delta2;
 | 
			
		||||
	out [13] += in[2]*delta + in[2-half_width]*delta2;
 | 
			
		||||
	out [14] += in[1]*delta + in[1-half_width]*delta2;
 | 
			
		||||
	out [15] += in[0]*delta + in[0-half_width]*delta2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
 | 
			
		||||
{
 | 
			
		||||
	unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
 | 
			
		||||
	buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
 | 
			
		||||
	
 | 
			
		||||
	int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
 | 
			
		||||
	int delta2 = delta * interp;
 | 
			
		||||
	
 | 
			
		||||
	/* Fails if buffer size was exceeded */
 | 
			
		||||
	assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
 | 
			
		||||
	
 | 
			
		||||
	out [7] += delta * delta_unit - delta2;
 | 
			
		||||
	out [8] += delta2;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								src/ext/blip_buf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/ext/blip_buf.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
/** \file
 | 
			
		||||
Sample buffer that resamples from input clock rate to output sample rate */
 | 
			
		||||
 | 
			
		||||
/* blip_buf $vers */
 | 
			
		||||
#ifndef BLIP_BUF_H 
 | 
			
		||||
#define BLIP_BUF_H
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
	extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/** First parameter of most functions is blip_t*, or const blip_t* if nothing
 | 
			
		||||
is changed. */
 | 
			
		||||
typedef struct blip_t blip_t;
 | 
			
		||||
 | 
			
		||||
/** Creates new buffer that can hold at most sample_count samples. Sets rates
 | 
			
		||||
so that there are blip_max_ratio clocks per sample. Returns pointer to new
 | 
			
		||||
buffer, or NULL if insufficient memory. */
 | 
			
		||||
blip_t* blip_new( int sample_count );
 | 
			
		||||
 | 
			
		||||
/** Sets approximate input clock rate and output sample rate. For every
 | 
			
		||||
clock_rate input clocks, approximately sample_rate samples are generated. */
 | 
			
		||||
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
 | 
			
		||||
 | 
			
		||||
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
 | 
			
		||||
clock_rate must not be greater than sample_rate*blip_max_ratio. */
 | 
			
		||||
blip_max_ratio = 1 << 20 };
 | 
			
		||||
 | 
			
		||||
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
 | 
			
		||||
void blip_clear( blip_t* );
 | 
			
		||||
 | 
			
		||||
/** Adds positive/negative delta into buffer at specified clock time. */
 | 
			
		||||
void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
 | 
			
		||||
 | 
			
		||||
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
 | 
			
		||||
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
 | 
			
		||||
 | 
			
		||||
/** Length of time frame, in clocks, needed to make sample_count additional
 | 
			
		||||
samples available. */
 | 
			
		||||
int blip_clocks_needed( const blip_t*, int sample_count );
 | 
			
		||||
 | 
			
		||||
enum { /** Maximum number of samples that can be generated from one time frame. */
 | 
			
		||||
blip_max_frame = 4000 };
 | 
			
		||||
 | 
			
		||||
/** Makes input clocks before clock_duration available for reading as output
 | 
			
		||||
samples. Also begins new time frame at clock_duration, so that clock time 0 in
 | 
			
		||||
the new time frame specifies the same clock as clock_duration in the old time
 | 
			
		||||
frame specified. Deltas can have been added slightly past clock_duration (up to
 | 
			
		||||
however many clocks there are in two output samples). */
 | 
			
		||||
void blip_end_frame( blip_t*, unsigned int clock_duration );
 | 
			
		||||
 | 
			
		||||
/** Number of buffered samples available for reading. */
 | 
			
		||||
int blip_samples_avail( const blip_t* );
 | 
			
		||||
 | 
			
		||||
/** Reads and removes at most 'count' samples and writes them to 'out'. If
 | 
			
		||||
'stereo' is true, writes output to every other element of 'out', allowing easy
 | 
			
		||||
interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
 | 
			
		||||
samples. Returns number of samples actually read.  */
 | 
			
		||||
int blip_read_samples( blip_t*, short out [], int count);
 | 
			
		||||
 | 
			
		||||
/** Frees buffer. No effect if NULL is passed. */
 | 
			
		||||
void blip_delete( blip_t* );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Deprecated */
 | 
			
		||||
typedef blip_t blip_buffer_t;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										3590
									
								
								src/ext/duktape/duk_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3590
									
								
								src/ext/duktape/duk_config.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1815
									
								
								src/ext/duktape/duk_source_meta.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1815
									
								
								src/ext/duktape/duk_source_meta.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										93368
									
								
								src/ext/duktape/duktape.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93368
									
								
								src/ext/duktape/duktape.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1288
									
								
								src/ext/duktape/duktape.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1288
									
								
								src/ext/duktape/duktape.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										495
									
								
								src/ext/file_dialog.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										495
									
								
								src/ext/file_dialog.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,495 @@
 | 
			
		||||
// MIT License
 | 
			
		||||
 | 
			
		||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
 | 
			
		||||
 | 
			
		||||
// 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 "file_dialog.h"
 | 
			
		||||
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
 | 
			
		||||
#if defined(__WINDOWS__)
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <commdlg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#define UTF8ToString(S) (wchar_t *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
 | 
			
		||||
#define StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(wchar_t))
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
 | 
			
		||||
{
 | 
			
		||||
	OPENFILENAMEW ofn;
 | 
			
		||||
	SDL_zero(ofn);
 | 
			
		||||
 | 
			
		||||
	wchar_t filename[MAX_PATH];
 | 
			
		||||
	memset(filename, 0, sizeof(filename));
 | 
			
		||||
 | 
			
		||||
	ofn.lStructSize = sizeof(ofn);
 | 
			
		||||
	ofn.lpstrFile = filename;
 | 
			
		||||
	ofn.nMaxFile = sizeof(filename);
 | 
			
		||||
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
 | 
			
		||||
 | 
			
		||||
	if(GetOpenFileNameW(&ofn))
 | 
			
		||||
	{
 | 
			
		||||
		FILE* source = _wfopen(filename, L"rb");
 | 
			
		||||
		if (source)
 | 
			
		||||
		{
 | 
			
		||||
			fseek(source, 0, SEEK_END);
 | 
			
		||||
			s32 size = ftell(source);
 | 
			
		||||
			fseek(source, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
			u8* buffer = malloc(size);
 | 
			
		||||
 | 
			
		||||
			if(buffer)
 | 
			
		||||
				fread(buffer, size, 1, source);
 | 
			
		||||
 | 
			
		||||
			fclose(source);
 | 
			
		||||
 | 
			
		||||
			if(buffer)
 | 
			
		||||
			{
 | 
			
		||||
				const wchar_t* basename = wcsrchr(filename, L'\\');
 | 
			
		||||
				const wchar_t* name = basename ? basename + 1 : filename;
 | 
			
		||||
 | 
			
		||||
				callback(StringToUTF8(name), buffer, size, data, 0);
 | 
			
		||||
				free(buffer);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	callback(NULL, NULL, 0, data, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
 | 
			
		||||
{
 | 
			
		||||
	OPENFILENAMEW ofn;
 | 
			
		||||
	SDL_zero(ofn);
 | 
			
		||||
 | 
			
		||||
	wchar_t filename[MAX_PATH];
 | 
			
		||||
	memset(filename, 0, sizeof(filename));
 | 
			
		||||
	wcscpy(filename, UTF8ToString(name));
 | 
			
		||||
 | 
			
		||||
	ofn.lStructSize = sizeof(ofn);
 | 
			
		||||
	ofn.lpstrFile = filename;
 | 
			
		||||
	ofn.nMaxFile = sizeof(filename);
 | 
			
		||||
	ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
 | 
			
		||||
 | 
			
		||||
	if(GetSaveFileNameW(&ofn))
 | 
			
		||||
	{
 | 
			
		||||
		FILE* file = _wfopen(filename, L"wb");
 | 
			
		||||
 | 
			
		||||
		if(file)
 | 
			
		||||
		{
 | 
			
		||||
			fwrite(buffer, 1, size, file);
 | 
			
		||||
			fclose(file);
 | 
			
		||||
 | 
			
		||||
#if !defined(__WINDOWS__)
 | 
			
		||||
			chmod(filename, mode);
 | 
			
		||||
#endif
 | 
			
		||||
			callback(true, data);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	callback(false, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <shlobj.h>
 | 
			
		||||
 | 
			
		||||
const char* folder_dialog(void* data)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	BROWSEINFOW bi = { 0 };
 | 
			
		||||
	bi.lpszTitle  = L"Browse for folder...";
 | 
			
		||||
	bi.ulFlags    = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
 | 
			
		||||
	bi.lParam     = (LPARAM) NULL;
 | 
			
		||||
 | 
			
		||||
	LPITEMIDLIST pidl = SHBrowseForFolderW ( &bi );
 | 
			
		||||
 | 
			
		||||
	if ( pidl != 0 )
 | 
			
		||||
	{
 | 
			
		||||
		wchar_t path[MAX_PATH];
 | 
			
		||||
		SHGetPathFromIDListW (pidl, path);
 | 
			
		||||
 | 
			
		||||
		LPMALLOC imalloc = NULL;
 | 
			
		||||
		if ( SUCCEEDED( SHGetMalloc ( &imalloc )) )
 | 
			
		||||
		{
 | 
			
		||||
			imalloc->lpVtbl->Free (imalloc, pidl );
 | 
			
		||||
			imalloc->lpVtbl->Release (imalloc);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			static char result[MAX_PATH];
 | 
			
		||||
			strcpy(result, StringToUTF8(path));
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#elif defined(__EMSCRIPTEN__)
 | 
			
		||||
 | 
			
		||||
#include <emscripten.h>
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
 | 
			
		||||
{
 | 
			
		||||
	EM_ASM_
 | 
			
		||||
	({
 | 
			
		||||
		Module.showAddPopup(function(filename, rom)
 | 
			
		||||
		{
 | 
			
		||||
			if(filename == null || rom == null)
 | 
			
		||||
			{
 | 
			
		||||
				Runtime.dynCall('viiiii', $0, [0, 0, 0, $1, 0]);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				var filePtr = Module._malloc(filename.length + 1);
 | 
			
		||||
				stringToUTF8(filename, filePtr, filename.length + 1);
 | 
			
		||||
 | 
			
		||||
				var dataPtr = Module._malloc(rom.length);
 | 
			
		||||
				writeArrayToMemory(rom, dataPtr);
 | 
			
		||||
 | 
			
		||||
				Runtime.dynCall('viiiii', $0, [filePtr, dataPtr, rom.length, $1, 0]);
 | 
			
		||||
 | 
			
		||||
				Module._free(filePtr);
 | 
			
		||||
				Module._free(dataPtr);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}, callback, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
 | 
			
		||||
{
 | 
			
		||||
	EM_ASM_
 | 
			
		||||
	({
 | 
			
		||||
		var name = Pointer_stringify($0);
 | 
			
		||||
		var blob = new Blob([HEAPU8.subarray($1, $1 + $2)], {type: "application/octet-stream"});
 | 
			
		||||
 | 
			
		||||
		Module.saveAs(blob, name);
 | 
			
		||||
	}, name, buffer, size);
 | 
			
		||||
 | 
			
		||||
	callback(true, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#elif defined(__LINUX__)
 | 
			
		||||
 | 
			
		||||
#if defined(__ARM_LINUX__)
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data) {}
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode) {}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <gtk/gtk.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
static void WaitForCleanup(void)
 | 
			
		||||
{
 | 
			
		||||
	while (gtk_events_pending())
 | 
			
		||||
		gtk_main_iteration();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
 | 
			
		||||
{
 | 
			
		||||
	bool done = false;
 | 
			
		||||
 | 
			
		||||
	if(gtk_init_check(NULL,NULL))
 | 
			
		||||
	{
 | 
			
		||||
		GtkWidget *dialog = gtk_file_chooser_dialog_new("Open File", NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
 | 
			
		||||
			"_Cancel", GTK_RESPONSE_CANCEL,
 | 
			
		||||
			"_Open", GTK_RESPONSE_ACCEPT,
 | 
			
		||||
			NULL);
 | 
			
		||||
 | 
			
		||||
		if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT )
 | 
			
		||||
		{
 | 
			
		||||
			char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 | 
			
		||||
 | 
			
		||||
			FILE* source = fopen(filename, "rb");
 | 
			
		||||
			if (source)
 | 
			
		||||
			{
 | 
			
		||||
				fseek(source, 0, SEEK_END);
 | 
			
		||||
				s32 size = ftell(source);
 | 
			
		||||
				fseek(source, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
				u8* buffer = malloc(size);
 | 
			
		||||
 | 
			
		||||
				if(buffer && fread(buffer, size, 1, source));
 | 
			
		||||
 | 
			
		||||
				fclose(source);
 | 
			
		||||
 | 
			
		||||
				if(buffer)
 | 
			
		||||
				{
 | 
			
		||||
					const char* basename = strrchr(filename, '/');
 | 
			
		||||
 | 
			
		||||
					mode_t mode = 0;
 | 
			
		||||
					{
 | 
			
		||||
						struct stat s;
 | 
			
		||||
						if(stat(filename, &s) == 0)
 | 
			
		||||
							mode = s.st_mode;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					callback(basename ? basename + 1 : filename, buffer, size, data, mode);
 | 
			
		||||
					free(buffer);
 | 
			
		||||
 | 
			
		||||
					done = true;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		WaitForCleanup();
 | 
			
		||||
		gtk_widget_destroy(dialog);
 | 
			
		||||
		WaitForCleanup();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!done)
 | 
			
		||||
		callback(NULL, NULL, 0, data, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
 | 
			
		||||
{
 | 
			
		||||
	bool done = false;
 | 
			
		||||
 | 
			
		||||
	if(gtk_init_check(NULL,NULL))
 | 
			
		||||
	{
 | 
			
		||||
		GtkWidget *dialog = gtk_file_chooser_dialog_new("Save File", NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
 | 
			
		||||
			"_Cancel", GTK_RESPONSE_CANCEL,
 | 
			
		||||
			"_Save", GTK_RESPONSE_ACCEPT,
 | 
			
		||||
			NULL);
 | 
			
		||||
 | 
			
		||||
		gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
 | 
			
		||||
		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), name);
 | 
			
		||||
 | 
			
		||||
		if(gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT)
 | 
			
		||||
		{
 | 
			
		||||
			char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 | 
			
		||||
 | 
			
		||||
			FILE* file = fopen(filename, "wb");
 | 
			
		||||
 | 
			
		||||
			if(file)
 | 
			
		||||
			{
 | 
			
		||||
				fwrite(buffer, 1, size, file);
 | 
			
		||||
				fclose(file);
 | 
			
		||||
 | 
			
		||||
				chmod(filename, mode);
 | 
			
		||||
 | 
			
		||||
				callback(true, data);
 | 
			
		||||
				done = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		WaitForCleanup();
 | 
			
		||||
		gtk_widget_destroy(dialog);
 | 
			
		||||
		WaitForCleanup();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!done)
 | 
			
		||||
		callback(false, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#elif defined(__MACOSX__)
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
bool file_dialog_load_path(char* buffer);
 | 
			
		||||
bool file_dialog_save_path(const char* name, char* buffer);
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data) 
 | 
			
		||||
{
 | 
			
		||||
	bool done = false;
 | 
			
		||||
 | 
			
		||||
	char filename[FILENAME_MAX];
 | 
			
		||||
	if(file_dialog_load_path(filename))
 | 
			
		||||
	{
 | 
			
		||||
		FILE* source = fopen(filename, "rb");
 | 
			
		||||
		if (source)
 | 
			
		||||
		{
 | 
			
		||||
			fseek(source, 0, SEEK_END);
 | 
			
		||||
			s32 size = ftell(source);
 | 
			
		||||
			fseek(source, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
			u8* buffer = malloc(size);
 | 
			
		||||
 | 
			
		||||
			if(buffer && fread(buffer, size, 1, source)){}
 | 
			
		||||
 | 
			
		||||
			fclose(source);
 | 
			
		||||
 | 
			
		||||
			if(buffer)
 | 
			
		||||
			{
 | 
			
		||||
				const char* basename = strrchr(filename, '/');
 | 
			
		||||
 | 
			
		||||
				mode_t mode = 0;
 | 
			
		||||
				{
 | 
			
		||||
					struct stat s;
 | 
			
		||||
					if(stat(filename, &s) == 0)
 | 
			
		||||
						mode = s.st_mode;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				callback(basename ? basename + 1 : filename, buffer, size, data, mode);
 | 
			
		||||
				free(buffer);
 | 
			
		||||
 | 
			
		||||
				done = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!done)
 | 
			
		||||
		callback(NULL, NULL, 0, data, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode) 
 | 
			
		||||
{
 | 
			
		||||
	bool done = false;
 | 
			
		||||
 | 
			
		||||
	char filename[FILENAME_MAX];
 | 
			
		||||
	if(file_dialog_save_path(name, filename))
 | 
			
		||||
	{
 | 
			
		||||
		FILE* file = fopen(filename, "wb");
 | 
			
		||||
 | 
			
		||||
		if(file)
 | 
			
		||||
		{
 | 
			
		||||
			fwrite(buffer, 1, size, file);
 | 
			
		||||
			fclose(file);
 | 
			
		||||
 | 
			
		||||
			chmod(filename, mode);
 | 
			
		||||
 | 
			
		||||
			callback(true, data);
 | 
			
		||||
			done = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!done)
 | 
			
		||||
		callback(false, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#elif defined(__ANDROID__)
 | 
			
		||||
 | 
			
		||||
#include <jni.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
 | 
			
		||||
{
 | 
			
		||||
	JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
 | 
			
		||||
	jobject activity = (jobject)SDL_AndroidGetActivity();
 | 
			
		||||
	jclass clazz = (*env)->GetObjectClass(env, activity);
 | 
			
		||||
 | 
			
		||||
	jmethodID method_id = (*env)->GetMethodID(env, clazz, "loadFile", "()Ljava/lang/String;");
 | 
			
		||||
	jstring jPath = (jstring) (*env)->CallObjectMethod(env, activity, method_id);
 | 
			
		||||
 | 
			
		||||
	const char *filename = (*env)->GetStringUTFChars(env, jPath, NULL);
 | 
			
		||||
 | 
			
		||||
	(*env)->DeleteLocalRef(env, activity);
 | 
			
		||||
	(*env)->DeleteLocalRef(env, clazz);
 | 
			
		||||
 | 
			
		||||
	bool done = false;
 | 
			
		||||
 | 
			
		||||
	if(filename && strlen(filename))
 | 
			
		||||
	{
 | 
			
		||||
		FILE* source = fopen(filename, "rb");
 | 
			
		||||
		if (source)
 | 
			
		||||
		{
 | 
			
		||||
			fseek(source, 0, SEEK_END);
 | 
			
		||||
			s32 size = ftell(source);
 | 
			
		||||
			fseek(source, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
			u8* buffer = malloc(size);
 | 
			
		||||
 | 
			
		||||
			if(buffer && fread(buffer, size, 1, source)){}
 | 
			
		||||
 | 
			
		||||
			fclose(source);
 | 
			
		||||
 | 
			
		||||
			if(buffer)
 | 
			
		||||
			{
 | 
			
		||||
				const char* basename = strrchr(filename, '/');
 | 
			
		||||
 | 
			
		||||
				mode_t mode = 0;
 | 
			
		||||
				{
 | 
			
		||||
					struct stat s;
 | 
			
		||||
					if(stat(filename, &s) == 0)
 | 
			
		||||
						mode = s.st_mode;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				callback(basename ? basename + 1 : filename, buffer, size, data, mode);
 | 
			
		||||
				free(buffer);
 | 
			
		||||
 | 
			
		||||
				done = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!done)
 | 
			
		||||
		callback(NULL, NULL, 0, data, 0);
 | 
			
		||||
 | 
			
		||||
	(*env)->ReleaseStringUTFChars(env, jPath, filename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode) 
 | 
			
		||||
{
 | 
			
		||||
	JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
 | 
			
		||||
	jobject activity = (jobject)SDL_AndroidGetActivity();
 | 
			
		||||
	jclass clazz = (*env)->GetObjectClass(env, activity);
 | 
			
		||||
 | 
			
		||||
	jstring jName = (*env)->NewStringUTF(env, name); 
 | 
			
		||||
	jmethodID method_id = (*env)->GetMethodID(env, clazz, "saveFile", "(Ljava/lang/String;)Ljava/lang/String;");
 | 
			
		||||
	jstring jPath = (jstring) (*env)->CallObjectMethod(env, activity, method_id, jName);
 | 
			
		||||
 | 
			
		||||
	const char *filename = (*env)->GetStringUTFChars(env, jPath, NULL);
 | 
			
		||||
 | 
			
		||||
	(*env)->DeleteLocalRef(env, jName);
 | 
			
		||||
	(*env)->DeleteLocalRef(env, activity);
 | 
			
		||||
	(*env)->DeleteLocalRef(env, clazz);
 | 
			
		||||
 | 
			
		||||
	bool done = false;
 | 
			
		||||
 | 
			
		||||
	if(filename && strlen(filename))
 | 
			
		||||
	{
 | 
			
		||||
		FILE* file = fopen(filename, "wb");
 | 
			
		||||
 | 
			
		||||
		if(file)
 | 
			
		||||
		{
 | 
			
		||||
			fwrite(buffer, 1, size, file);
 | 
			
		||||
			fclose(file);
 | 
			
		||||
 | 
			
		||||
			chmod(filename, mode);
 | 
			
		||||
 | 
			
		||||
			callback(true, data);
 | 
			
		||||
			done = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!done)
 | 
			
		||||
		callback(false, data);
 | 
			
		||||
 | 
			
		||||
	(*env)->ReleaseStringUTFChars(env, jPath, filename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data) {}
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode) {}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										145
									
								
								src/ext/file_dialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/ext/file_dialog.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
// MIT License
 | 
			
		||||
 | 
			
		||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
 | 
			
		||||
 | 
			
		||||
// 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 "file_dialog.h"
 | 
			
		||||
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
 | 
			
		||||
#if defined(__WINRT__)
 | 
			
		||||
 | 
			
		||||
#include <ppltasks.h>
 | 
			
		||||
#include <Robuffer.h>
 | 
			
		||||
#include <wrl/client.h>
 | 
			
		||||
#include <collection.h>
 | 
			
		||||
 | 
			
		||||
using namespace Platform;
 | 
			
		||||
using namespace Concurrency;
 | 
			
		||||
using namespace Windows::Storage;
 | 
			
		||||
using namespace Windows::Storage::Streams;
 | 
			
		||||
using namespace Windows::Storage::Pickers;
 | 
			
		||||
using namespace Microsoft::WRL;
 | 
			
		||||
 | 
			
		||||
#define UTF8ToString(S) (wchar_t *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
 | 
			
		||||
#define StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(wchar_t))
 | 
			
		||||
 | 
			
		||||
byte* GetPointerToData(IBuffer^ buffer)
 | 
			
		||||
{
 | 
			
		||||
	Object^ obj = buffer;
 | 
			
		||||
	ComPtr<IInspectable> insp(reinterpret_cast<IInspectable*>(obj));
 | 
			
		||||
	ComPtr<IBufferByteAccess> bufferByteAccess;
 | 
			
		||||
	insp.As(&bufferByteAccess);
 | 
			
		||||
	byte* data = nullptr;
 | 
			
		||||
	bufferByteAccess->Buffer(&data);
 | 
			
		||||
 | 
			
		||||
	return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IBuffer^ IBufferFromArray(Array<unsigned char>^ data)
 | 
			
		||||
{
 | 
			
		||||
	DataWriter^ dataWriter = ref new DataWriter();
 | 
			
		||||
	dataWriter->WriteBytes(data);
 | 
			
		||||
	return dataWriter->DetachBuffer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IBuffer^ IBufferFromPointer(LPBYTE pbData, DWORD cbData)
 | 
			
		||||
{
 | 
			
		||||
	auto byteArray = new ArrayReference<unsigned char>(pbData, cbData);
 | 
			
		||||
	return IBufferFromArray(reinterpret_cast<Array<unsigned char>^>(byteArray));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
 | 
			
		||||
{
 | 
			
		||||
	FileOpenPicker^ openPicker = ref new FileOpenPicker();
 | 
			
		||||
 | 
			
		||||
	openPicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
 | 
			
		||||
	openPicker->FileTypeFilter->Append("*");
 | 
			
		||||
 | 
			
		||||
	create_task(openPicker->PickSingleFileAsync()).then([callback, data](StorageFile^ file)
 | 
			
		||||
	{
 | 
			
		||||
		if (file)
 | 
			
		||||
		{
 | 
			
		||||
			create_task(file->OpenAsync(FileAccessMode::Read)).then([callback, data, file](IRandomAccessStream^ stream)
 | 
			
		||||
			{
 | 
			
		||||
				Buffer^ buffer = ref new Buffer((int)stream->Size);
 | 
			
		||||
 | 
			
		||||
				create_task(stream->ReadAsync(buffer, buffer->Capacity, InputStreamOptions::Partial)).then([callback, data, file](IBuffer^ buffer)
 | 
			
		||||
				{
 | 
			
		||||
					auto ptr = GetPointerToData(buffer);
 | 
			
		||||
					auto size = buffer->Length;
 | 
			
		||||
 | 
			
		||||
					std::wstring fooW(file->Name->Begin());
 | 
			
		||||
					callback(StringToUTF8(fooW.c_str()), ptr, size, data, 0);
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			callback(NULL, NULL, 0, data, 0);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
 | 
			
		||||
{
 | 
			
		||||
	FileSavePicker^ savePicker = ref new FileSavePicker();
 | 
			
		||||
 | 
			
		||||
	savePicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
 | 
			
		||||
 | 
			
		||||
	savePicker->SuggestedFileName = ref new Platform::String(UTF8ToString(name));
 | 
			
		||||
 | 
			
		||||
	auto allExtensions = ref new Platform::Collections::Vector<String^>();
 | 
			
		||||
	allExtensions->Append(".tic");
 | 
			
		||||
	allExtensions->Append(".html");
 | 
			
		||||
	allExtensions->Append(".gif");
 | 
			
		||||
 | 
			
		||||
	savePicker->FileTypeChoices->Insert("TIC files", allExtensions);
 | 
			
		||||
 | 
			
		||||
	create_task(savePicker->PickSaveFileAsync()).then([callback, data, buffer, size](StorageFile^ file)
 | 
			
		||||
	{
 | 
			
		||||
		if (file)
 | 
			
		||||
		{
 | 
			
		||||
			create_task(file->OpenAsync(FileAccessMode::ReadWrite)).then([callback, data, file, buffer, size](IRandomAccessStream^ stream)
 | 
			
		||||
			{
 | 
			
		||||
				auto ibuffer = IBufferFromPointer(const_cast<u8*>(buffer), (DWORD)size);
 | 
			
		||||
 | 
			
		||||
				if (ibuffer)
 | 
			
		||||
				{
 | 
			
		||||
					create_task(FileIO::WriteBufferAsync(file, ibuffer)).then([callback, data, file]()
 | 
			
		||||
					{
 | 
			
		||||
						callback(true, data);
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
				else callback(false, data);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		else callback(false, data);
 | 
			
		||||
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* folder_dialog(void* data)
 | 
			
		||||
{
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										41
									
								
								src/ext/file_dialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/ext/file_dialog.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
// MIT License
 | 
			
		||||
 | 
			
		||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <tic80_types.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef void(*file_dialog_load_callback)(const char* name, const u8* buffer, s32 size, void* data, u32 mode);
 | 
			
		||||
typedef void(*file_dialog_save_callback)(bool result, void* data);
 | 
			
		||||
 | 
			
		||||
void file_dialog_load(file_dialog_load_callback callback, void* data);
 | 
			
		||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode);
 | 
			
		||||
const char* folder_dialog(void* data);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										76
									
								
								src/ext/file_dialog.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/ext/file_dialog.m
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
// MIT License
 | 
			
		||||
 | 
			
		||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
 | 
			
		||||
 | 
			
		||||
// 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 "file_dialog.h"
 | 
			
		||||
 | 
			
		||||
#include <AppKit/AppKit.h>
 | 
			
		||||
 | 
			
		||||
bool file_dialog_load_path(char* buffer)
 | 
			
		||||
{
 | 
			
		||||
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 | 
			
		||||
	NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];    
 | 
			
		||||
	NSOpenPanel *dialog = [NSOpenPanel openPanel];
 | 
			
		||||
	[dialog setAllowsMultipleSelection:NO];
 | 
			
		||||
 | 
			
		||||
	bool success = false;
 | 
			
		||||
	if ( [dialog runModal] == NSModalResponseOK )
 | 
			
		||||
	{
 | 
			
		||||
		NSURL *url = [dialog URL];
 | 
			
		||||
		const char *utf8Path = [[url path] UTF8String];
 | 
			
		||||
 | 
			
		||||
		strcpy( buffer, utf8Path);
 | 
			
		||||
 | 
			
		||||
		success = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[pool release];
 | 
			
		||||
	[keyWindow makeKeyAndOrderFront:nil];
 | 
			
		||||
 | 
			
		||||
	return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool file_dialog_save_path(const char* name, char* buffer)
 | 
			
		||||
{
 | 
			
		||||
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 | 
			
		||||
	NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; 
 | 
			
		||||
	NSSavePanel *dialog = [NSSavePanel savePanel];
 | 
			
		||||
	[dialog setExtensionHidden:NO];
 | 
			
		||||
 | 
			
		||||
	NSString *nameString = [NSString stringWithUTF8String: name];
 | 
			
		||||
	[dialog setNameFieldStringValue:nameString];
 | 
			
		||||
 | 
			
		||||
	bool success = false;
 | 
			
		||||
	if ( [dialog runModal] == NSModalResponseOK )
 | 
			
		||||
	{
 | 
			
		||||
		NSURL *url = [dialog URL];
 | 
			
		||||
		const char *utf8Path = [[url path] UTF8String];
 | 
			
		||||
 | 
			
		||||
		strcpy( buffer, utf8Path);
 | 
			
		||||
 | 
			
		||||
		success = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[pool release];
 | 
			
		||||
	[keyWindow makeKeyAndOrderFront:nil];
 | 
			
		||||
 | 
			
		||||
	return success;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										421
									
								
								src/ext/gif.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										421
									
								
								src/ext/gif.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,421 @@
 | 
			
		||||
// MIT License
 | 
			
		||||
 | 
			
		||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
 | 
			
		||||
 | 
			
		||||
// 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 <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "gif.h"
 | 
			
		||||
#include "gif_lib.h"
 | 
			
		||||
 | 
			
		||||
static gif_image* readGif(GifFileType *gif)
 | 
			
		||||
{
 | 
			
		||||
	gif_image* image = NULL;
 | 
			
		||||
 | 
			
		||||
	s32 error = 0;
 | 
			
		||||
 | 
			
		||||
	if(gif)
 | 
			
		||||
	{
 | 
			
		||||
		if(gif->SHeight > 0 && gif->SWidth > 0)
 | 
			
		||||
		{
 | 
			
		||||
			s32 size = gif->SWidth * gif->SHeight * sizeof(GifPixelType);
 | 
			
		||||
			GifPixelType* screen = (GifPixelType*)malloc(size);
 | 
			
		||||
 | 
			
		||||
			if(screen)
 | 
			
		||||
			{
 | 
			
		||||
				memset(screen, gif->SBackGroundColor, size);
 | 
			
		||||
 | 
			
		||||
				GifRecordType record = UNDEFINED_RECORD_TYPE;
 | 
			
		||||
 | 
			
		||||
				do
 | 
			
		||||
				{
 | 
			
		||||
					if(DGifGetRecordType(gif, &record) == GIF_ERROR)
 | 
			
		||||
					{
 | 
			
		||||
						error = gif->Error;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					switch (record)
 | 
			
		||||
					{
 | 
			
		||||
					case IMAGE_DESC_RECORD_TYPE:
 | 
			
		||||
						{
 | 
			
		||||
							if(DGifGetImageDesc(gif) == GIF_ERROR)
 | 
			
		||||
								error = gif->Error;
 | 
			
		||||
 | 
			
		||||
							s32 row = gif->Image.Top;
 | 
			
		||||
							s32 col = gif->Image.Left;
 | 
			
		||||
							s32 width = gif->Image.Width;
 | 
			
		||||
							s32 height = gif->Image.Height;
 | 
			
		||||
 | 
			
		||||
							if (gif->Image.Left + gif->Image.Width > gif->SWidth ||
 | 
			
		||||
								gif->Image.Top + gif->Image.Height > gif->SHeight)
 | 
			
		||||
									error = E_GIF_ERR_OPEN_FAILED;
 | 
			
		||||
 | 
			
		||||
							if (gif->Image.Interlace)
 | 
			
		||||
							{
 | 
			
		||||
								s32 InterlacedOffset[] = { 0, 4, 2, 1 };
 | 
			
		||||
								s32 InterlacedJumps[] = { 8, 8, 4, 2 };
 | 
			
		||||
 | 
			
		||||
								for (s32 i = 0; i < 4; i++)
 | 
			
		||||
									for (s32 j = row + InterlacedOffset[i]; j < row + height; j += InterlacedJumps[i])
 | 
			
		||||
									{
 | 
			
		||||
										if(DGifGetLine(gif, screen + j * gif->SWidth + col, width) == GIF_ERROR)
 | 
			
		||||
										{
 | 
			
		||||
											error = gif->Error;
 | 
			
		||||
											break;
 | 
			
		||||
										}
 | 
			
		||||
									}
 | 
			
		||||
							}
 | 
			
		||||
							else
 | 
			
		||||
							{
 | 
			
		||||
								for (s32 i = 0; i < height; i++, row++)
 | 
			
		||||
								{
 | 
			
		||||
									if(DGifGetLine(gif, screen + row * gif->SWidth + col, width) == GIF_ERROR)
 | 
			
		||||
									{
 | 
			
		||||
										error = gif->Error;
 | 
			
		||||
										break;
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
 | 
			
		||||
					case EXTENSION_RECORD_TYPE:
 | 
			
		||||
						{
 | 
			
		||||
							s32 extCode = 0;
 | 
			
		||||
							GifByteType* extension = NULL;
 | 
			
		||||
 | 
			
		||||
							if (DGifGetExtension(gif, &extCode, &extension) == GIF_ERROR)
 | 
			
		||||
								error = gif->Error;
 | 
			
		||||
							else
 | 
			
		||||
							{
 | 
			
		||||
								while (extension != NULL)
 | 
			
		||||
								{
 | 
			
		||||
									if(DGifGetExtensionNext(gif, &extension) == GIF_ERROR)
 | 
			
		||||
									{
 | 
			
		||||
										error = gif->Error;
 | 
			
		||||
										break;
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
					case TERMINATE_RECORD_TYPE:
 | 
			
		||||
						break;
 | 
			
		||||
					default: break;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if(error != E_GIF_SUCCEEDED)
 | 
			
		||||
						break;
 | 
			
		||||
				}
 | 
			
		||||
				while(record != TERMINATE_RECORD_TYPE);
 | 
			
		||||
 | 
			
		||||
				if(error == E_GIF_SUCCEEDED)
 | 
			
		||||
				{
 | 
			
		||||
 | 
			
		||||
					image = (gif_image*)malloc(sizeof(gif_image));
 | 
			
		||||
 | 
			
		||||
					if(image)
 | 
			
		||||
					{
 | 
			
		||||
						memset(image, 0, sizeof(gif_image));
 | 
			
		||||
						image->buffer = screen;
 | 
			
		||||
						image->width = gif->SWidth;
 | 
			
		||||
						image->height = gif->SHeight;
 | 
			
		||||
 | 
			
		||||
						ColorMapObject* colorMap = gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
 | 
			
		||||
 | 
			
		||||
						image->colors = colorMap->ColorCount;
 | 
			
		||||
 | 
			
		||||
						s32 size = image->colors * sizeof(gif_color);
 | 
			
		||||
						image->palette = malloc(size);
 | 
			
		||||
 | 
			
		||||
						memcpy(image->palette, colorMap->Colors, size);
 | 
			
		||||
					}					
 | 
			
		||||
				}
 | 
			
		||||
				else free(screen);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		DGifCloseFile(gif, &error);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return image;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	const void* data;
 | 
			
		||||
	s32 pos;
 | 
			
		||||
} GifBuffer;
 | 
			
		||||
 | 
			
		||||
static int readBuffer(GifFileType* gif, GifByteType* data, int size)
 | 
			
		||||
{
 | 
			
		||||
	GifBuffer* buffer = (GifBuffer*)gif->UserData;
 | 
			
		||||
 | 
			
		||||
	memcpy(data, (const u8*)buffer->data + buffer->pos, size);
 | 
			
		||||
	buffer->pos += size;
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gif_image* gif_read_data(const void* data, s32 size)
 | 
			
		||||
{
 | 
			
		||||
	GifBuffer buffer = {data, 0};
 | 
			
		||||
	GifFileType *gif = DGifOpen(&buffer, readBuffer, NULL);
 | 
			
		||||
 | 
			
		||||
	return readGif(gif);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool writeGif(GifFileType* gif, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp)
 | 
			
		||||
{
 | 
			
		||||
	bool result = false;
 | 
			
		||||
	s32 error = 0;
 | 
			
		||||
 | 
			
		||||
	if(gif)
 | 
			
		||||
	{
 | 
			
		||||
		s32 colors = 1 << bpp;
 | 
			
		||||
		ColorMapObject* colorMap = GifMakeMapObject(colors, NULL);
 | 
			
		||||
 | 
			
		||||
		memcpy(colorMap->Colors, palette, colors * sizeof(GifColorType));
 | 
			
		||||
 | 
			
		||||
		if(EGifPutScreenDesc(gif, width, height, bpp, 0, colorMap) != GIF_ERROR)
 | 
			
		||||
		{
 | 
			
		||||
			if(EGifPutImageDesc(gif, 0, 0, width, height, false, NULL) != GIF_ERROR)
 | 
			
		||||
			{
 | 
			
		||||
				GifByteType* ptr = (GifByteType*)data;
 | 
			
		||||
				for (s32 i = 0; i < height; i++, ptr += width)
 | 
			
		||||
				{
 | 
			
		||||
					if (EGifPutLine(gif, ptr, width) == GIF_ERROR)
 | 
			
		||||
					{
 | 
			
		||||
						error = gif->Error;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				result = error == E_GIF_SUCCEEDED;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		EGifCloseFile(gif, &error);
 | 
			
		||||
		GifFreeMapObject(colorMap);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int writeBuffer(GifFileType* gif, const GifByteType* data, int size)
 | 
			
		||||
{
 | 
			
		||||
	GifBuffer* buffer = (GifBuffer*)gif->UserData;
 | 
			
		||||
 | 
			
		||||
	memcpy((u8*)buffer->data + buffer->pos, data, size);
 | 
			
		||||
	buffer->pos += size;
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gif_write_data(const void* buffer, s32* size, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp)
 | 
			
		||||
{
 | 
			
		||||
	s32 error = 0;
 | 
			
		||||
	GifBuffer output = {buffer, 0};
 | 
			
		||||
	GifFileType* gif = EGifOpen(&output, writeBuffer, &error);
 | 
			
		||||
 | 
			
		||||
	bool result = writeGif(gif, width, height, data, palette, bpp);
 | 
			
		||||
 | 
			
		||||
	*size = output.pos;
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gif_close(gif_image* image)
 | 
			
		||||
{
 | 
			
		||||
	if(image)
 | 
			
		||||
	{
 | 
			
		||||
		if(image->buffer) free(image->buffer);
 | 
			
		||||
		if(image->palette) free(image->palette);
 | 
			
		||||
 | 
			
		||||
		free(image);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool AddLoop(GifFileType *gif)
 | 
			
		||||
{
 | 
			
		||||
	{
 | 
			
		||||
		const char *nsle = "NETSCAPE2.0";
 | 
			
		||||
		const char subblock[] = {
 | 
			
		||||
			1, // always 1
 | 
			
		||||
			0, // little-endian loop counter:
 | 
			
		||||
			0  // 0 for infinite loop.
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		EGifPutExtensionLeader(gif, APPLICATION_EXT_FUNC_CODE);
 | 
			
		||||
		EGifPutExtensionBlock(gif, 11, nsle);
 | 
			
		||||
		EGifPutExtensionBlock(gif, 3, subblock);
 | 
			
		||||
		EGifPutExtensionTrailer(gif);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const u8* toColor(const u8* ptr, gif_color* color)
 | 
			
		||||
{
 | 
			
		||||
	color->b = *ptr++;
 | 
			
		||||
	color->g = *ptr++;
 | 
			
		||||
	color->r = *ptr++;
 | 
			
		||||
	ptr++;
 | 
			
		||||
 | 
			
		||||
	return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gif_write_animation(const void* buffer, s32* size, s32 width, s32 height, const u8* data, s32 frames, s32 fps, s32 scale)
 | 
			
		||||
{
 | 
			
		||||
	bool result = false;
 | 
			
		||||
 | 
			
		||||
	s32 swidth = width*scale, sheight = height*scale;
 | 
			
		||||
	s32 frameSize = width * height;
 | 
			
		||||
 | 
			
		||||
	enum{Bpp = 8, PalSize = 1 << Bpp, PalStructSize = PalSize * sizeof(gif_color)};
 | 
			
		||||
 | 
			
		||||
	s32 error = 0;
 | 
			
		||||
	GifBuffer output = {buffer, 0};
 | 
			
		||||
	GifFileType* gif = EGifOpen(&output, writeBuffer, &error);
 | 
			
		||||
 | 
			
		||||
	if(gif)
 | 
			
		||||
	{			
 | 
			
		||||
		EGifSetGifVersion(gif, true);
 | 
			
		||||
 | 
			
		||||
		if(EGifPutScreenDesc(gif, swidth, sheight, Bpp, 0, NULL) != GIF_ERROR)
 | 
			
		||||
		{
 | 
			
		||||
			if(AddLoop(gif))
 | 
			
		||||
			{
 | 
			
		||||
				gif_color* palette = (gif_color*)malloc(PalStructSize);					
 | 
			
		||||
				u8* screen = malloc(frameSize);
 | 
			
		||||
				u8* line = malloc(swidth);
 | 
			
		||||
 | 
			
		||||
				for(s32 f = 0; f < frames; f++)
 | 
			
		||||
				{
 | 
			
		||||
					enum {DelayUnits = 100, MinDelay = 2};
 | 
			
		||||
 | 
			
		||||
					s32 frame = (f * fps * MinDelay * 2 + 1) / (2 * DelayUnits);
 | 
			
		||||
 | 
			
		||||
					if(frame >= frames)
 | 
			
		||||
						break;
 | 
			
		||||
 | 
			
		||||
					s32 colors = 0;
 | 
			
		||||
					const u8* ptr = data + frameSize*frame*sizeof(u32);
 | 
			
		||||
					
 | 
			
		||||
					{
 | 
			
		||||
						memset(palette, 0, PalStructSize);
 | 
			
		||||
						memset(screen, 0, frameSize);
 | 
			
		||||
 | 
			
		||||
						for(s32 i = 0; i < frameSize; i++)
 | 
			
		||||
						{
 | 
			
		||||
							if(colors >= PalSize) break;
 | 
			
		||||
 | 
			
		||||
							gif_color color;
 | 
			
		||||
							toColor(ptr + i*sizeof(u32), &color);
 | 
			
		||||
 | 
			
		||||
							bool found = false;
 | 
			
		||||
							for(s32 c = 0; c < colors; c++)
 | 
			
		||||
							{
 | 
			
		||||
								if(memcmp(&palette[c], &color, sizeof(gif_color)) == 0)
 | 
			
		||||
								{
 | 
			
		||||
									found = true;
 | 
			
		||||
									screen[i] = c;
 | 
			
		||||
									break;
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if(!found)
 | 
			
		||||
							{
 | 
			
		||||
								// TODO: check for last color in palette and try to find closest color
 | 
			
		||||
								screen[i] = colors;
 | 
			
		||||
								memcpy(&palette[colors], &color, sizeof(gif_color));
 | 
			
		||||
								colors++;
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					{
 | 
			
		||||
						GraphicsControlBlock gcb = 
 | 
			
		||||
						{
 | 
			
		||||
							.DisposalMode = DISPOSE_DO_NOT,
 | 
			
		||||
							.UserInputFlag = false,
 | 
			
		||||
							.DelayTime = MinDelay,
 | 
			
		||||
							.TransparentColor = -1,
 | 
			
		||||
						};
 | 
			
		||||
 | 
			
		||||
						u8 ext[4];
 | 
			
		||||
						EGifGCBToExtension(&gcb, ext);
 | 
			
		||||
						EGifPutExtension(gif, GRAPHICS_EXT_FUNC_CODE, sizeof ext, ext);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					ColorMapObject* colorMap = GifMakeMapObject(PalSize, NULL);
 | 
			
		||||
					memset(colorMap->Colors, 0, PalStructSize);
 | 
			
		||||
					memcpy(colorMap->Colors, palette, colors * sizeof(GifColorType));
 | 
			
		||||
 | 
			
		||||
					if(EGifPutImageDesc(gif, 0, 0, swidth, sheight, false, colorMap) != GIF_ERROR)
 | 
			
		||||
					{
 | 
			
		||||
						for(s32 y = 0; y < height; y++)
 | 
			
		||||
						{
 | 
			
		||||
							for(s32 x = 0, pos = y*width; x < width; x++, pos++)
 | 
			
		||||
							{
 | 
			
		||||
								u8 color = screen[pos];
 | 
			
		||||
								for(s32 s = 0, pos = x*scale; s < scale; s++, pos++)
 | 
			
		||||
									line[pos] = color;
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							for(s32 s = 0; s < scale; s++)
 | 
			
		||||
							{
 | 
			
		||||
								if (EGifPutLine(gif, line, swidth) == GIF_ERROR)
 | 
			
		||||
								{
 | 
			
		||||
									error = gif->Error;
 | 
			
		||||
									break;
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if(error != E_GIF_SUCCEEDED) break;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						*size = output.pos;
 | 
			
		||||
 | 
			
		||||
						result = error == E_GIF_SUCCEEDED;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					GifFreeMapObject(colorMap);
 | 
			
		||||
 | 
			
		||||
					if(!result)
 | 
			
		||||
						break;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				free(line);
 | 
			
		||||
				free(screen);
 | 
			
		||||
				free(palette);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		EGifCloseFile(gif, &error);
 | 
			
		||||
 | 
			
		||||
		*size = output.pos;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								src/ext/gif.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/ext/gif.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
// MIT License
 | 
			
		||||
 | 
			
		||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <tic80_types.h>
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	u8 r;
 | 
			
		||||
	u8 g;
 | 
			
		||||
	u8 b;
 | 
			
		||||
}gif_color;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	u8* buffer;
 | 
			
		||||
 | 
			
		||||
	gif_color* palette;
 | 
			
		||||
 | 
			
		||||
	s32 width;
 | 
			
		||||
	s32 height;
 | 
			
		||||
 | 
			
		||||
	s32 colors;
 | 
			
		||||
} gif_image;
 | 
			
		||||
 | 
			
		||||
gif_image* gif_read_data(const void* buffer, s32 size);
 | 
			
		||||
bool gif_write_data(const void* buffer, s32* size, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp);
 | 
			
		||||
bool gif_write_animation(const void* buffer, s32* size, s32 width, s32 height, const u8* data, s32 frames, s32 fps, s32 scale);
 | 
			
		||||
void gif_close(gif_image* image);
 | 
			
		||||
							
								
								
									
										287
									
								
								src/ext/md5.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								src/ext/md5.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,287 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
 | 
			
		||||
 * MD5 Message-Digest Algorithm (RFC 1321).
 | 
			
		||||
 *
 | 
			
		||||
 * Homepage:
 | 
			
		||||
 * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
 | 
			
		||||
 *
 | 
			
		||||
 * Author:
 | 
			
		||||
 * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This software was written by Alexander Peslyak in 2001.  No copyright is
 | 
			
		||||
 * claimed, and the software is hereby placed in the public domain.
 | 
			
		||||
 * In case this attempt to disclaim copyright and place the software in the
 | 
			
		||||
 * public domain is deemed null and void, then the software is
 | 
			
		||||
 * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
 | 
			
		||||
 * general public under the following terms:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted.
 | 
			
		||||
 *
 | 
			
		||||
 * There's ABSOLUTELY NO WARRANTY, express or implied.
 | 
			
		||||
 *
 | 
			
		||||
 * (This is a heavily cut-down "BSD license".)
 | 
			
		||||
 *
 | 
			
		||||
 * This differs from Colin Plumb's older public domain implementation in that
 | 
			
		||||
 * no exactly 32-bit integer data type is required (any 32-bit or wider
 | 
			
		||||
 * unsigned integer data type will do), there's no compile-time endianness
 | 
			
		||||
 * configuration, and the function prototypes match OpenSSL's.  No code from
 | 
			
		||||
 * Colin Plumb's implementation has been reused; this comment merely compares
 | 
			
		||||
 * the properties of the two independent implementations.
 | 
			
		||||
 *
 | 
			
		||||
 * The primary goals of this implementation are portability and ease of use.
 | 
			
		||||
 * It is meant to be fast, but not as fast as possible.  Some known
 | 
			
		||||
 * optimizations are not included to reduce source code size and avoid
 | 
			
		||||
 * compile-time configuration.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 
 | 
			
		||||
#include "md5.h"
 | 
			
		||||
 
 | 
			
		||||
/*
 | 
			
		||||
 * The basic MD5 functions.
 | 
			
		||||
 *
 | 
			
		||||
 * F and G are optimized compared to their RFC 1321 definitions for
 | 
			
		||||
 * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
 | 
			
		||||
 * implementation.
 | 
			
		||||
 */
 | 
			
		||||
#define F(x, y, z)			((z) ^ ((x) & ((y) ^ (z))))
 | 
			
		||||
#define G(x, y, z)			((y) ^ ((z) & ((x) ^ (y))))
 | 
			
		||||
#define H(x, y, z)			(((x) ^ (y)) ^ (z))
 | 
			
		||||
#define H2(x, y, z)			((x) ^ ((y) ^ (z)))
 | 
			
		||||
#define I(x, y, z)			((y) ^ ((x) | ~(z)))
 | 
			
		||||
 
 | 
			
		||||
/*
 | 
			
		||||
 * The MD5 transformation for all four rounds.
 | 
			
		||||
 */
 | 
			
		||||
#define STEP(f, a, b, c, d, x, t, s) \
 | 
			
		||||
	(a) += f((b), (c), (d)) + (x) + (t); \
 | 
			
		||||
	(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
 | 
			
		||||
	(a) += (b);
 | 
			
		||||
 
 | 
			
		||||
/*
 | 
			
		||||
 * SET reads 4 input bytes in little-endian byte order and stores them in a
 | 
			
		||||
 * properly aligned word in host byte order.
 | 
			
		||||
 *
 | 
			
		||||
 * The check for little-endian architectures that tolerate unaligned memory
 | 
			
		||||
 * accesses is just an optimization.  Nothing will break if it fails to detect
 | 
			
		||||
 * a suitable architecture.
 | 
			
		||||
 *
 | 
			
		||||
 * Unfortunately, this optimization may be a C strict aliasing rules violation
 | 
			
		||||
 * if the caller's data buffer has effective type that cannot be aliased by
 | 
			
		||||
 * MD5_u32plus.  In practice, this problem may occur if these MD5 routines are
 | 
			
		||||
 * inlined into a calling function, or with future and dangerously advanced
 | 
			
		||||
 * link-time optimizations.  For the time being, keeping these MD5 routines in
 | 
			
		||||
 * their own translation unit avoids the problem.
 | 
			
		||||
 */
 | 
			
		||||
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
 | 
			
		||||
#define SET(n) \
 | 
			
		||||
	(*(MD5_u32plus *)&ptr[(n) * 4])
 | 
			
		||||
#define GET(n) \
 | 
			
		||||
	SET(n)
 | 
			
		||||
#else
 | 
			
		||||
#define SET(n) \
 | 
			
		||||
	(ctx->block[(n)] = \
 | 
			
		||||
	(MD5_u32plus)ptr[(n) * 4] | \
 | 
			
		||||
	((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
 | 
			
		||||
	((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
 | 
			
		||||
	((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
 | 
			
		||||
#define GET(n) \
 | 
			
		||||
	(ctx->block[(n)])
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
/*
 | 
			
		||||
 * This processes one or more 64-byte data blocks, but does NOT update the bit
 | 
			
		||||
 * counters.  There are no alignment requirements.
 | 
			
		||||
 */
 | 
			
		||||
static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	const unsigned char *ptr;
 | 
			
		||||
	MD5_u32plus a, b, c, d;
 | 
			
		||||
	MD5_u32plus saved_a, saved_b, saved_c, saved_d;
 | 
			
		||||
 
 | 
			
		||||
	ptr = (const unsigned char *)data;
 | 
			
		||||
 
 | 
			
		||||
	a = ctx->a;
 | 
			
		||||
	b = ctx->b;
 | 
			
		||||
	c = ctx->c;
 | 
			
		||||
	d = ctx->d;
 | 
			
		||||
 
 | 
			
		||||
	do {
 | 
			
		||||
		saved_a = a;
 | 
			
		||||
		saved_b = b;
 | 
			
		||||
		saved_c = c;
 | 
			
		||||
		saved_d = d;
 | 
			
		||||
 
 | 
			
		||||
/* Round 1 */
 | 
			
		||||
		STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
 | 
			
		||||
		STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
 | 
			
		||||
		STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
 | 
			
		||||
		STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
 | 
			
		||||
		STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
 | 
			
		||||
		STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
 | 
			
		||||
		STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
 | 
			
		||||
		STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
 | 
			
		||||
		STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
 | 
			
		||||
		STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
 | 
			
		||||
		STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
 | 
			
		||||
		STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
 | 
			
		||||
		STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
 | 
			
		||||
		STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
 | 
			
		||||
		STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
 | 
			
		||||
		STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
 | 
			
		||||
 
 | 
			
		||||
/* Round 2 */
 | 
			
		||||
		STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
 | 
			
		||||
		STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
 | 
			
		||||
		STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
 | 
			
		||||
		STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
 | 
			
		||||
		STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
 | 
			
		||||
		STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
 | 
			
		||||
		STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
 | 
			
		||||
		STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
 | 
			
		||||
		STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
 | 
			
		||||
		STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
 | 
			
		||||
		STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
 | 
			
		||||
		STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
 | 
			
		||||
		STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
 | 
			
		||||
		STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
 | 
			
		||||
		STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
 | 
			
		||||
		STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
 | 
			
		||||
 
 | 
			
		||||
/* Round 3 */
 | 
			
		||||
		STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
 | 
			
		||||
		STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
 | 
			
		||||
		STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
 | 
			
		||||
		STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
 | 
			
		||||
		STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
 | 
			
		||||
		STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
 | 
			
		||||
		STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
 | 
			
		||||
		STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
 | 
			
		||||
		STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
 | 
			
		||||
		STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
 | 
			
		||||
		STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
 | 
			
		||||
		STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
 | 
			
		||||
		STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
 | 
			
		||||
		STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
 | 
			
		||||
		STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
 | 
			
		||||
		STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
 | 
			
		||||
 
 | 
			
		||||
/* Round 4 */
 | 
			
		||||
		STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
 | 
			
		||||
		STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
 | 
			
		||||
		STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
 | 
			
		||||
		STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
 | 
			
		||||
		STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
 | 
			
		||||
		STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
 | 
			
		||||
		STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
 | 
			
		||||
		STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
 | 
			
		||||
		STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
 | 
			
		||||
		STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
 | 
			
		||||
		STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
 | 
			
		||||
		STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
 | 
			
		||||
		STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
 | 
			
		||||
		STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
 | 
			
		||||
		STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
 | 
			
		||||
		STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
 | 
			
		||||
 
 | 
			
		||||
		a += saved_a;
 | 
			
		||||
		b += saved_b;
 | 
			
		||||
		c += saved_c;
 | 
			
		||||
		d += saved_d;
 | 
			
		||||
 
 | 
			
		||||
		ptr += 64;
 | 
			
		||||
	} while (size -= 64);
 | 
			
		||||
 
 | 
			
		||||
	ctx->a = a;
 | 
			
		||||
	ctx->b = b;
 | 
			
		||||
	ctx->c = c;
 | 
			
		||||
	ctx->d = d;
 | 
			
		||||
 
 | 
			
		||||
	return ptr;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
void MD5_Init(MD5_CTX *ctx)
 | 
			
		||||
{
 | 
			
		||||
	ctx->a = 0x67452301;
 | 
			
		||||
	ctx->b = 0xefcdab89;
 | 
			
		||||
	ctx->c = 0x98badcfe;
 | 
			
		||||
	ctx->d = 0x10325476;
 | 
			
		||||
 
 | 
			
		||||
	ctx->lo = 0;
 | 
			
		||||
	ctx->hi = 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	MD5_u32plus saved_lo;
 | 
			
		||||
	unsigned long used, available;
 | 
			
		||||
 
 | 
			
		||||
	saved_lo = ctx->lo;
 | 
			
		||||
	if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
 | 
			
		||||
		ctx->hi++;
 | 
			
		||||
	ctx->hi += size >> 29;
 | 
			
		||||
 
 | 
			
		||||
	used = saved_lo & 0x3f;
 | 
			
		||||
 
 | 
			
		||||
	if (used) {
 | 
			
		||||
		available = 64 - used;
 | 
			
		||||
 
 | 
			
		||||
		if (size < available) {
 | 
			
		||||
			memcpy(&ctx->buffer[used], data, size);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
		memcpy(&ctx->buffer[used], data, available);
 | 
			
		||||
		data = (const unsigned char *)data + available;
 | 
			
		||||
		size -= available;
 | 
			
		||||
		body(ctx, ctx->buffer, 64);
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
	if (size >= 64) {
 | 
			
		||||
		data = body(ctx, data, size & ~(unsigned long)0x3f);
 | 
			
		||||
		size &= 0x3f;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
	memcpy(ctx->buffer, data, size);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
#define OUT(dst, src) \
 | 
			
		||||
	(dst)[0] = (unsigned char)(src); \
 | 
			
		||||
	(dst)[1] = (unsigned char)((src) >> 8); \
 | 
			
		||||
	(dst)[2] = (unsigned char)((src) >> 16); \
 | 
			
		||||
	(dst)[3] = (unsigned char)((src) >> 24);
 | 
			
		||||
 
 | 
			
		||||
void MD5_Final(unsigned char *result, MD5_CTX *ctx)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long used, available;
 | 
			
		||||
 
 | 
			
		||||
	used = ctx->lo & 0x3f;
 | 
			
		||||
 
 | 
			
		||||
	ctx->buffer[used++] = 0x80;
 | 
			
		||||
 
 | 
			
		||||
	available = 64 - used;
 | 
			
		||||
 
 | 
			
		||||
	if (available < 8) {
 | 
			
		||||
		memset(&ctx->buffer[used], 0, available);
 | 
			
		||||
		body(ctx, ctx->buffer, 64);
 | 
			
		||||
		used = 0;
 | 
			
		||||
		available = 64;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
	memset(&ctx->buffer[used], 0, available - 8);
 | 
			
		||||
 
 | 
			
		||||
	ctx->lo <<= 3;
 | 
			
		||||
	OUT(&ctx->buffer[56], ctx->lo)
 | 
			
		||||
	OUT(&ctx->buffer[60], ctx->hi)
 | 
			
		||||
 
 | 
			
		||||
	body(ctx, ctx->buffer, 64);
 | 
			
		||||
 
 | 
			
		||||
	OUT(&result[0], ctx->a)
 | 
			
		||||
	OUT(&result[4], ctx->b)
 | 
			
		||||
	OUT(&result[8], ctx->c)
 | 
			
		||||
	OUT(&result[12], ctx->d)
 | 
			
		||||
 
 | 
			
		||||
	memset(ctx, 0, sizeof(*ctx));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								src/ext/md5.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/ext/md5.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
 | 
			
		||||
 * MD5 Message-Digest Algorithm (RFC 1321).
 | 
			
		||||
 *
 | 
			
		||||
 * Homepage:
 | 
			
		||||
 * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
 | 
			
		||||
 *
 | 
			
		||||
 * Author:
 | 
			
		||||
 * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This software was written by Alexander Peslyak in 2001.  No copyright is
 | 
			
		||||
 * claimed, and the software is hereby placed in the public domain.
 | 
			
		||||
 * In case this attempt to disclaim copyright and place the software in the
 | 
			
		||||
 * public domain is deemed null and void, then the software is
 | 
			
		||||
 * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
 | 
			
		||||
 * general public under the following terms:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted.
 | 
			
		||||
 *
 | 
			
		||||
 * There's ABSOLUTELY NO WARRANTY, express or implied.
 | 
			
		||||
 *
 | 
			
		||||
 * See md5.c for more information.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#ifndef _MD5_H
 | 
			
		||||
#define _MD5_H
 | 
			
		||||
 
 | 
			
		||||
/* Any 32-bit or wider unsigned integer data type will do */
 | 
			
		||||
typedef unsigned int MD5_u32plus;
 | 
			
		||||
 
 | 
			
		||||
typedef struct {
 | 
			
		||||
	MD5_u32plus lo, hi;
 | 
			
		||||
	MD5_u32plus a, b, c, d;
 | 
			
		||||
	unsigned char buffer[64];
 | 
			
		||||
	MD5_u32plus block[16];
 | 
			
		||||
} MD5_CTX;
 | 
			
		||||
 
 | 
			
		||||
extern void MD5_Init(MD5_CTX *ctx);
 | 
			
		||||
extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
 | 
			
		||||
extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
 | 
			
		||||
 
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										14398
									
								
								src/ext/moonscript.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14398
									
								
								src/ext/moonscript.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										443
									
								
								src/ext/net/SDL_net.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										443
									
								
								src/ext/net/SDL_net.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,443 @@
 | 
			
		||||
/*
 | 
			
		||||
  SDL_net:  An example cross-platform network library for use with SDL
 | 
			
		||||
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
 | 
			
		||||
  Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
 | 
			
		||||
 | 
			
		||||
  This software is provided 'as-is', without any express or implied
 | 
			
		||||
  warranty.  In no event will the authors be held liable for any damages
 | 
			
		||||
  arising from the use of this software.
 | 
			
		||||
 | 
			
		||||
  Permission is granted to anyone to use this software for any purpose,
 | 
			
		||||
  including commercial applications, and to alter it and redistribute it
 | 
			
		||||
  freely, subject to the following restrictions:
 | 
			
		||||
 | 
			
		||||
  1. The origin of this software must not be misrepresented; you must not
 | 
			
		||||
     claim that you wrote the original software. If you use this software
 | 
			
		||||
     in a product, an acknowledgment in the product documentation would be
 | 
			
		||||
     appreciated but is not required.
 | 
			
		||||
  2. Altered source versions must be plainly marked as such, and must not be
 | 
			
		||||
     misrepresented as being the original software.
 | 
			
		||||
  3. This notice may not be removed or altered from any source distribution.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* $Id$ */
 | 
			
		||||
 | 
			
		||||
#ifndef _SDL_NET_H
 | 
			
		||||
#define _SDL_NET_H
 | 
			
		||||
 | 
			
		||||
#ifdef WITHOUT_SDL
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
typedef uint8_t Uint8;
 | 
			
		||||
typedef uint16_t Uint16;
 | 
			
		||||
typedef uint32_t Uint32;
 | 
			
		||||
 | 
			
		||||
typedef struct SDLNet_version {
 | 
			
		||||
    Uint8 major;
 | 
			
		||||
    Uint8 minor;
 | 
			
		||||
    Uint8 patch;
 | 
			
		||||
} SDLNet_version;
 | 
			
		||||
 | 
			
		||||
#else /* WITHOUT_SDL */
 | 
			
		||||
 | 
			
		||||
#include "SDL.h"
 | 
			
		||||
#include "SDL_endian.h"
 | 
			
		||||
#include "SDL_version.h"
 | 
			
		||||
 | 
			
		||||
typedef SDL_version SDLNet_version;
 | 
			
		||||
 | 
			
		||||
#endif /* WITHOUT_SDL */
 | 
			
		||||
 | 
			
		||||
#include "begin_code.h"
 | 
			
		||||
 | 
			
		||||
/* Set up for C function definitions, even when using C++ */
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
 | 
			
		||||
*/
 | 
			
		||||
#define SDL_NET_MAJOR_VERSION   2
 | 
			
		||||
#define SDL_NET_MINOR_VERSION   0
 | 
			
		||||
#define SDL_NET_PATCHLEVEL      1
 | 
			
		||||
 | 
			
		||||
/* This macro can be used to fill a version structure with the compile-time
 | 
			
		||||
 * version of the SDL_net library.
 | 
			
		||||
 */
 | 
			
		||||
#define SDL_NET_VERSION(X)                          \
 | 
			
		||||
{                                                   \
 | 
			
		||||
    (X)->major = SDL_NET_MAJOR_VERSION;             \
 | 
			
		||||
    (X)->minor = SDL_NET_MINOR_VERSION;             \
 | 
			
		||||
    (X)->patch = SDL_NET_PATCHLEVEL;                \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function gets the version of the dynamically linked SDL_net library.
 | 
			
		||||
   it should NOT be used to fill a version structure, instead you should
 | 
			
		||||
   use the SDL_NET_VERSION() macro.
 | 
			
		||||
 */
 | 
			
		||||
extern DECLSPEC const SDLNet_version * SDLCALL SDLNet_Linked_Version(void);
 | 
			
		||||
 | 
			
		||||
/* Initialize/Cleanup the network API
 | 
			
		||||
   SDL must be initialized before calls to functions in this library,
 | 
			
		||||
   because this library uses utility functions from the SDL library.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int  SDLCALL SDLNet_Init(void);
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_Quit(void);
 | 
			
		||||
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
/* IPv4 hostname resolution API                                        */
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    Uint32 host;            /* 32-bit IPv4 host address */
 | 
			
		||||
    Uint16 port;            /* 16-bit protocol port */
 | 
			
		||||
} IPaddress;
 | 
			
		||||
 | 
			
		||||
/* Resolve a host name and port to an IP address in network form.
 | 
			
		||||
   If the function succeeds, it will return 0.
 | 
			
		||||
   If the host couldn't be resolved, the host portion of the returned
 | 
			
		||||
   address will be INADDR_NONE, and the function will return -1.
 | 
			
		||||
   If 'host' is NULL, the resolved host will be set to INADDR_ANY.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef INADDR_ANY
 | 
			
		||||
#define INADDR_ANY      0x00000000
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef INADDR_NONE
 | 
			
		||||
#define INADDR_NONE     0xFFFFFFFF
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef INADDR_LOOPBACK
 | 
			
		||||
#define INADDR_LOOPBACK     0x7f000001
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef INADDR_BROADCAST
 | 
			
		||||
#define INADDR_BROADCAST    0xFFFFFFFF
 | 
			
		||||
#endif
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_ResolveHost(IPaddress *address, const char *host, Uint16 port);
 | 
			
		||||
 | 
			
		||||
/* Resolve an ip address to a host name in canonical form.
 | 
			
		||||
   If the ip couldn't be resolved, this function returns NULL,
 | 
			
		||||
   otherwise a pointer to a static buffer containing the hostname
 | 
			
		||||
   is returned.  Note that this function is not thread-safe.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC const char * SDLCALL SDLNet_ResolveIP(const IPaddress *ip);
 | 
			
		||||
 | 
			
		||||
/* Get the addresses of network interfaces on this system.
 | 
			
		||||
   This returns the number of addresses saved in 'addresses'
 | 
			
		||||
 */
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_GetLocalAddresses(IPaddress *addresses, int maxcount);
 | 
			
		||||
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
/* TCP network API                                                     */
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
 | 
			
		||||
typedef struct _TCPsocket *TCPsocket;
 | 
			
		||||
 | 
			
		||||
/* Open a TCP network socket
 | 
			
		||||
   If ip.host is INADDR_NONE or INADDR_ANY, this creates a local server
 | 
			
		||||
   socket on the given port, otherwise a TCP connection to the remote
 | 
			
		||||
   host and port is attempted. The address passed in should already be
 | 
			
		||||
   swapped to network byte order (addresses returned from
 | 
			
		||||
   SDLNet_ResolveHost() are already in the correct form).
 | 
			
		||||
   The newly created socket is returned, or NULL if there was an error.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC TCPsocket SDLCALL SDLNet_TCP_Open(IPaddress *ip);
 | 
			
		||||
 | 
			
		||||
/* Accept an incoming connection on the given server socket.
 | 
			
		||||
   The newly created socket is returned, or NULL if there was an error.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC TCPsocket SDLCALL SDLNet_TCP_Accept(TCPsocket server);
 | 
			
		||||
 | 
			
		||||
/* Get the IP address of the remote system associated with the socket.
 | 
			
		||||
   If the socket is a server socket, this function returns NULL.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC IPaddress * SDLCALL SDLNet_TCP_GetPeerAddress(TCPsocket sock);
 | 
			
		||||
 | 
			
		||||
/* Send 'len' bytes of 'data' over the non-server socket 'sock'
 | 
			
		||||
   This function returns the actual amount of data sent.  If the return value
 | 
			
		||||
   is less than the amount of data sent, then either the remote connection was
 | 
			
		||||
   closed, or an unknown socket error occurred.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_TCP_Send(TCPsocket sock, const void *data,
 | 
			
		||||
        int len);
 | 
			
		||||
 | 
			
		||||
/* Receive up to 'maxlen' bytes of data over the non-server socket 'sock',
 | 
			
		||||
   and store them in the buffer pointed to by 'data'.
 | 
			
		||||
   This function returns the actual amount of data received.  If the return
 | 
			
		||||
   value is less than or equal to zero, then either the remote connection was
 | 
			
		||||
   closed, or an unknown socket error occurred.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_TCP_Recv(TCPsocket sock, void *data, int maxlen);
 | 
			
		||||
 | 
			
		||||
/* Close a TCP network socket */
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_TCP_Close(TCPsocket sock);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
/* UDP network API                                                     */
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
 | 
			
		||||
/* The maximum channels on a a UDP socket */
 | 
			
		||||
#define SDLNET_MAX_UDPCHANNELS  32
 | 
			
		||||
/* The maximum addresses bound to a single UDP socket channel */
 | 
			
		||||
#define SDLNET_MAX_UDPADDRESSES 4
 | 
			
		||||
 | 
			
		||||
typedef struct _UDPsocket *UDPsocket;
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int channel;        /* The src/dst channel of the packet */
 | 
			
		||||
    Uint8 *data;        /* The packet data */
 | 
			
		||||
    int len;            /* The length of the packet data */
 | 
			
		||||
    int maxlen;         /* The size of the data buffer */
 | 
			
		||||
    int status;         /* packet status after sending */
 | 
			
		||||
    IPaddress address;  /* The source/dest address of an incoming/outgoing packet */
 | 
			
		||||
} UDPpacket;
 | 
			
		||||
 | 
			
		||||
/* Allocate/resize/free a single UDP packet 'size' bytes long.
 | 
			
		||||
   The new packet is returned, or NULL if the function ran out of memory.
 | 
			
		||||
 */
 | 
			
		||||
extern DECLSPEC UDPpacket * SDLCALL SDLNet_AllocPacket(int size);
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_ResizePacket(UDPpacket *packet, int newsize);
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_FreePacket(UDPpacket *packet);
 | 
			
		||||
 | 
			
		||||
/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets,
 | 
			
		||||
   each 'size' bytes long.
 | 
			
		||||
   A pointer to the first packet in the array is returned, or NULL if the
 | 
			
		||||
   function ran out of memory.
 | 
			
		||||
 */
 | 
			
		||||
extern DECLSPEC UDPpacket ** SDLCALL SDLNet_AllocPacketV(int howmany, int size);
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_FreePacketV(UDPpacket **packetV);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Open a UDP network socket
 | 
			
		||||
   If 'port' is non-zero, the UDP socket is bound to a local port.
 | 
			
		||||
   The 'port' should be given in native byte order, but is used
 | 
			
		||||
   internally in network (big endian) byte order, in addresses, etc.
 | 
			
		||||
   This allows other systems to send to this socket via a known port.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC UDPsocket SDLCALL SDLNet_UDP_Open(Uint16 port);
 | 
			
		||||
 | 
			
		||||
/* Set the percentage of simulated packet loss for packets sent on the socket.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_UDP_SetPacketLoss(UDPsocket sock, int percent);
 | 
			
		||||
 | 
			
		||||
/* Bind the address 'address' to the requested channel on the UDP socket.
 | 
			
		||||
   If the channel is -1, then the first unbound channel that has not yet
 | 
			
		||||
   been bound to the maximum number of addresses will be bound with
 | 
			
		||||
   the given address as it's primary address.
 | 
			
		||||
   If the channel is already bound, this new address will be added to the
 | 
			
		||||
   list of valid source addresses for packets arriving on the channel.
 | 
			
		||||
   If the channel is not already bound, then the address becomes the primary
 | 
			
		||||
   address, to which all outbound packets on the channel are sent.
 | 
			
		||||
   This function returns the channel which was bound, or -1 on error.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address);
 | 
			
		||||
 | 
			
		||||
/* Unbind all addresses from the given channel */
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_UDP_Unbind(UDPsocket sock, int channel);
 | 
			
		||||
 | 
			
		||||
/* Get the primary IP address of the remote system associated with the
 | 
			
		||||
   socket and channel.  If the channel is -1, then the primary IP port
 | 
			
		||||
   of the UDP socket is returned -- this is only meaningful for sockets
 | 
			
		||||
   opened with a specific port.
 | 
			
		||||
   If the channel is not bound and not -1, this function returns NULL.
 | 
			
		||||
 */
 | 
			
		||||
extern DECLSPEC IPaddress * SDLCALL SDLNet_UDP_GetPeerAddress(UDPsocket sock, int channel);
 | 
			
		||||
 | 
			
		||||
/* Send a vector of packets to the the channels specified within the packet.
 | 
			
		||||
   If the channel specified in the packet is -1, the packet will be sent to
 | 
			
		||||
   the address in the 'src' member of the packet.
 | 
			
		||||
   Each packet will be updated with the status of the packet after it has
 | 
			
		||||
   been sent, -1 if the packet send failed.
 | 
			
		||||
   This function returns the number of packets sent.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_UDP_SendV(UDPsocket sock, UDPpacket **packets, int npackets);
 | 
			
		||||
 | 
			
		||||
/* Send a single packet to the specified channel.
 | 
			
		||||
   If the channel specified in the packet is -1, the packet will be sent to
 | 
			
		||||
   the address in the 'src' member of the packet.
 | 
			
		||||
   The packet will be updated with the status of the packet after it has
 | 
			
		||||
   been sent.
 | 
			
		||||
   This function returns 1 if the packet was sent, or 0 on error.
 | 
			
		||||
 | 
			
		||||
   NOTE:
 | 
			
		||||
   The maximum size of the packet is limited by the MTU (Maximum Transfer Unit)
 | 
			
		||||
   of the transport medium.  It can be as low as 250 bytes for some PPP links,
 | 
			
		||||
   and as high as 1500 bytes for ethernet.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet);
 | 
			
		||||
 | 
			
		||||
/* Receive a vector of pending packets from the UDP socket.
 | 
			
		||||
   The returned packets contain the source address and the channel they arrived
 | 
			
		||||
   on.  If they did not arrive on a bound channel, the the channel will be set
 | 
			
		||||
   to -1.
 | 
			
		||||
   The channels are checked in highest to lowest order, so if an address is
 | 
			
		||||
   bound to multiple channels, the highest channel with the source address
 | 
			
		||||
   bound will be returned.
 | 
			
		||||
   This function returns the number of packets read from the network, or -1
 | 
			
		||||
   on error.  This function does not block, so can return 0 packets pending.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_UDP_RecvV(UDPsocket sock, UDPpacket **packets);
 | 
			
		||||
 | 
			
		||||
/* Receive a single packet from the UDP socket.
 | 
			
		||||
   The returned packet contains the source address and the channel it arrived
 | 
			
		||||
   on.  If it did not arrive on a bound channel, the the channel will be set
 | 
			
		||||
   to -1.
 | 
			
		||||
   The channels are checked in highest to lowest order, so if an address is
 | 
			
		||||
   bound to multiple channels, the highest channel with the source address
 | 
			
		||||
   bound will be returned.
 | 
			
		||||
   This function returns the number of packets read from the network, or -1
 | 
			
		||||
   on error.  This function does not block, so can return 0 packets pending.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_UDP_Recv(UDPsocket sock, UDPpacket *packet);
 | 
			
		||||
 | 
			
		||||
/* Close a UDP network socket */
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_UDP_Close(UDPsocket sock);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
/* Hooks for checking sockets for available data                       */
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
 | 
			
		||||
typedef struct _SDLNet_SocketSet *SDLNet_SocketSet;
 | 
			
		||||
 | 
			
		||||
/* Any network socket can be safely cast to this socket type */
 | 
			
		||||
typedef struct _SDLNet_GenericSocket {
 | 
			
		||||
    int ready;
 | 
			
		||||
} *SDLNet_GenericSocket;
 | 
			
		||||
 | 
			
		||||
/* Allocate a socket set for use with SDLNet_CheckSockets()
 | 
			
		||||
   This returns a socket set for up to 'maxsockets' sockets, or NULL if
 | 
			
		||||
   the function ran out of memory.
 | 
			
		||||
 */
 | 
			
		||||
extern DECLSPEC SDLNet_SocketSet SDLCALL SDLNet_AllocSocketSet(int maxsockets);
 | 
			
		||||
 | 
			
		||||
/* Add a socket to a set of sockets to be checked for available data */
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_AddSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock);
 | 
			
		||||
SDL_FORCE_INLINE int SDLNet_TCP_AddSocket(SDLNet_SocketSet set, TCPsocket sock)
 | 
			
		||||
{
 | 
			
		||||
    return SDLNet_AddSocket(set, (SDLNet_GenericSocket)sock);
 | 
			
		||||
}
 | 
			
		||||
SDL_FORCE_INLINE int SDLNet_UDP_AddSocket(SDLNet_SocketSet set, UDPsocket sock)
 | 
			
		||||
{
 | 
			
		||||
    return SDLNet_AddSocket(set, (SDLNet_GenericSocket)sock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Remove a socket from a set of sockets to be checked for available data */
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_DelSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock);
 | 
			
		||||
SDL_FORCE_INLINE int SDLNet_TCP_DelSocket(SDLNet_SocketSet set, TCPsocket sock)
 | 
			
		||||
{
 | 
			
		||||
    return SDLNet_DelSocket(set, (SDLNet_GenericSocket)sock);
 | 
			
		||||
}
 | 
			
		||||
SDL_FORCE_INLINE int SDLNet_UDP_DelSocket(SDLNet_SocketSet set, UDPsocket sock)
 | 
			
		||||
{
 | 
			
		||||
    return SDLNet_DelSocket(set, (SDLNet_GenericSocket)sock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function checks to see if data is available for reading on the
 | 
			
		||||
   given set of sockets.  If 'timeout' is 0, it performs a quick poll,
 | 
			
		||||
   otherwise the function returns when either data is available for
 | 
			
		||||
   reading, or the timeout in milliseconds has elapsed, which ever occurs
 | 
			
		||||
   first.  This function returns the number of sockets ready for reading,
 | 
			
		||||
   or -1 if there was an error with the select() system call.
 | 
			
		||||
*/
 | 
			
		||||
extern DECLSPEC int SDLCALL SDLNet_CheckSockets(SDLNet_SocketSet set, Uint32 timeout);
 | 
			
		||||
 | 
			
		||||
/* After calling SDLNet_CheckSockets(), you can use this function on a
 | 
			
		||||
   socket that was in the socket set, to find out if data is available
 | 
			
		||||
   for reading.
 | 
			
		||||
*/
 | 
			
		||||
#define SDLNet_SocketReady(sock) _SDLNet_SocketReady((SDLNet_GenericSocket)(sock))
 | 
			
		||||
SDL_FORCE_INLINE int _SDLNet_SocketReady(SDLNet_GenericSocket sock)
 | 
			
		||||
{
 | 
			
		||||
    return (sock != NULL) && (sock->ready);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Free a set of sockets allocated by SDL_NetAllocSocketSet() */
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_FreeSocketSet(SDLNet_SocketSet set);
 | 
			
		||||
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
/* Error reporting functions                                           */
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_SetError(const char *fmt, ...);
 | 
			
		||||
extern DECLSPEC const char * SDLCALL SDLNet_GetError(void);
 | 
			
		||||
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
/* Inline functions to read/write network data                         */
 | 
			
		||||
/***********************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Warning, some systems have data access alignment restrictions */
 | 
			
		||||
#if defined(sparc) || defined(mips) || defined(__arm__)
 | 
			
		||||
#define SDL_DATA_ALIGNED    1
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef SDL_DATA_ALIGNED
 | 
			
		||||
#define SDL_DATA_ALIGNED    0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Write a 16/32-bit value to network packet buffer */
 | 
			
		||||
#define SDLNet_Write16(value, areap) _SDLNet_Write16(value, areap)
 | 
			
		||||
#define SDLNet_Write32(value, areap) _SDLNet_Write32(value, areap)
 | 
			
		||||
 | 
			
		||||
/* Read a 16/32-bit value from network packet buffer */
 | 
			
		||||
#define SDLNet_Read16(areap) _SDLNet_Read16(areap)
 | 
			
		||||
#define SDLNet_Read32(areap) _SDLNet_Read32(areap)
 | 
			
		||||
 | 
			
		||||
#if !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED
 | 
			
		||||
 | 
			
		||||
SDL_FORCE_INLINE void _SDLNet_Write16(Uint16 value, void *areap)
 | 
			
		||||
{
 | 
			
		||||
    *(Uint16 *)areap = SDL_SwapBE16(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SDL_FORCE_INLINE void _SDLNet_Write32(Uint32 value, void *areap)
 | 
			
		||||
{
 | 
			
		||||
    *(Uint32 *)areap = SDL_SwapBE32(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SDL_FORCE_INLINE Uint16 _SDLNet_Read16(const void *areap)
 | 
			
		||||
{
 | 
			
		||||
    return SDL_SwapBE16(*(const Uint16 *)areap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SDL_FORCE_INLINE Uint32 _SDLNet_Read32(const void *areap)
 | 
			
		||||
{
 | 
			
		||||
    return SDL_SwapBE32(*(const Uint32 *)areap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else /* !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED */
 | 
			
		||||
 | 
			
		||||
SDL_FORCE_INLINE void _SDLNet_Write16(Uint16 value, void *areap)
 | 
			
		||||
{
 | 
			
		||||
    Uint8 *area = (Uint8*)areap;
 | 
			
		||||
    area[0] = (value >>  8) & 0xFF;
 | 
			
		||||
    area[1] =  value        & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SDL_FORCE_INLINE void _SDLNet_Write32(Uint32 value, void *areap)
 | 
			
		||||
{
 | 
			
		||||
    Uint8 *area = (Uint8*)areap;
 | 
			
		||||
    area[0] = (value >> 24) & 0xFF;
 | 
			
		||||
    area[1] = (value >> 16) & 0xFF;
 | 
			
		||||
    area[2] = (value >>  8) & 0xFF;
 | 
			
		||||
    area[3] =  value        & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SDL_FORCE_INLINE Uint16 _SDLNet_Read16(void *areap)
 | 
			
		||||
{
 | 
			
		||||
    Uint8 *area = (Uint8*)areap;
 | 
			
		||||
    return ((Uint16)area[0]) << 8 | ((Uint16)area[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SDL_FORCE_INLINE Uint32 _SDLNet_Read32(const void *areap)
 | 
			
		||||
{
 | 
			
		||||
    const Uint8 *area = (const Uint8*)areap;
 | 
			
		||||
    return ((Uint32)area[0]) << 24 | ((Uint32)area[1]) << 16 | ((Uint32)area[2]) << 8 | ((Uint32)area[3]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED */
 | 
			
		||||
 | 
			
		||||
/* Ends C function definitions when using C++ */
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#include "close_code.h"
 | 
			
		||||
 | 
			
		||||
#endif /* _SDL_NET_H */
 | 
			
		||||
							
								
								
									
										297
									
								
								src/ext/net/SDLnet.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								src/ext/net/SDLnet.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,297 @@
 | 
			
		||||
/*
 | 
			
		||||
  SDL_net:  An example cross-platform network library for use with SDL
 | 
			
		||||
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
 | 
			
		||||
  Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
 | 
			
		||||
 | 
			
		||||
  This software is provided 'as-is', without any express or implied
 | 
			
		||||
  warranty.  In no event will the authors be held liable for any damages
 | 
			
		||||
  arising from the use of this software.
 | 
			
		||||
 | 
			
		||||
  Permission is granted to anyone to use this software for any purpose,
 | 
			
		||||
  including commercial applications, and to alter it and redistribute it
 | 
			
		||||
  freely, subject to the following restrictions:
 | 
			
		||||
 | 
			
		||||
  1. The origin of this software must not be misrepresented; you must not
 | 
			
		||||
     claim that you wrote the original software. If you use this software
 | 
			
		||||
     in a product, an acknowledgment in the product documentation would be
 | 
			
		||||
     appreciated but is not required.
 | 
			
		||||
  2. Altered source versions must be plainly marked as such, and must not be
 | 
			
		||||
     misrepresented as being the original software.
 | 
			
		||||
  3. This notice may not be removed or altered from any source distribution.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* $Id$ */
 | 
			
		||||
 | 
			
		||||
#include "SDLnetsys.h"
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
 | 
			
		||||
#ifdef WITHOUT_SDL
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const SDLNet_version *SDLNet_Linked_Version(void)
 | 
			
		||||
{
 | 
			
		||||
    static SDLNet_version linked_version;
 | 
			
		||||
    SDL_NET_VERSION(&linked_version);
 | 
			
		||||
    return(&linked_version);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Since the UNIX/Win32/BeOS code is so different from MacOS,
 | 
			
		||||
   we'll just have two completely different sections here.
 | 
			
		||||
*/
 | 
			
		||||
static int SDLNet_started = 0;
 | 
			
		||||
 | 
			
		||||
#ifndef __USE_W32_SOCKETS
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef __USE_W32_SOCKETS
 | 
			
		||||
 | 
			
		||||
int SDLNet_GetLastError(void)
 | 
			
		||||
{
 | 
			
		||||
    return errno;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SDLNet_SetLastError(int err)
 | 
			
		||||
{
 | 
			
		||||
    errno = err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static char errorbuf[1024];
 | 
			
		||||
 | 
			
		||||
void SDLCALL SDLNet_SetError(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
    va_list argp;
 | 
			
		||||
    va_start(argp, fmt);
 | 
			
		||||
    SDL_vsnprintf(errorbuf, sizeof(errorbuf), fmt, argp);
 | 
			
		||||
    va_end(argp);
 | 
			
		||||
#ifndef WITHOUT_SDL
 | 
			
		||||
    SDL_SetError("%s", errorbuf);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char * SDLCALL SDLNet_GetError(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef WITHOUT_SDL
 | 
			
		||||
    return errorbuf;
 | 
			
		||||
#else
 | 
			
		||||
    return SDL_GetError();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize/Cleanup the network API */
 | 
			
		||||
int  SDLNet_Init(void)
 | 
			
		||||
{
 | 
			
		||||
    if ( !SDLNet_started ) {
 | 
			
		||||
#ifdef __USE_W32_SOCKETS
 | 
			
		||||
        /* Start up the windows networking */
 | 
			
		||||
        WORD version_wanted = MAKEWORD(1,1);
 | 
			
		||||
        WSADATA wsaData;
 | 
			
		||||
 | 
			
		||||
        if ( WSAStartup(version_wanted, &wsaData) != 0 ) {
 | 
			
		||||
            SDLNet_SetError("Couldn't initialize Winsock 1.1\n");
 | 
			
		||||
            return(-1);
 | 
			
		||||
        }
 | 
			
		||||
#else
 | 
			
		||||
        /* SIGPIPE is generated when a remote socket is closed */
 | 
			
		||||
        void (*handler)(int);
 | 
			
		||||
        handler = signal(SIGPIPE, SIG_IGN);
 | 
			
		||||
        if ( handler != SIG_DFL ) {
 | 
			
		||||
            signal(SIGPIPE, handler);
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    ++SDLNet_started;
 | 
			
		||||
    return(0);
 | 
			
		||||
}
 | 
			
		||||
void SDLNet_Quit(void)
 | 
			
		||||
{
 | 
			
		||||
    if ( SDLNet_started == 0 ) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if ( --SDLNet_started == 0 ) {
 | 
			
		||||
#ifdef __USE_W32_SOCKETS
 | 
			
		||||
        /* Clean up windows networking */
 | 
			
		||||
        if ( WSACleanup() == SOCKET_ERROR ) {
 | 
			
		||||
            if ( WSAGetLastError() == WSAEINPROGRESS ) {
 | 
			
		||||
#ifndef _WIN32_WCE
 | 
			
		||||
                //WSACancelBlockingCall();
 | 
			
		||||
#endif
 | 
			
		||||
                WSACleanup();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
#else
 | 
			
		||||
        /* Restore the SIGPIPE handler */
 | 
			
		||||
        void (*handler)(int);
 | 
			
		||||
        handler = signal(SIGPIPE, SIG_DFL);
 | 
			
		||||
        if ( handler != SIG_IGN ) {
 | 
			
		||||
            signal(SIGPIPE, handler);
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Resolve a host name and port to an IP address in network form */
 | 
			
		||||
int SDLNet_ResolveHost(IPaddress *address, const char *host, Uint16 port)
 | 
			
		||||
{
 | 
			
		||||
    int retval = 0;
 | 
			
		||||
 | 
			
		||||
    /* Perform the actual host resolution */
 | 
			
		||||
    if ( host == NULL ) {
 | 
			
		||||
        address->host = INADDR_ANY;
 | 
			
		||||
    } else {
 | 
			
		||||
        address->host = inet_addr(host);
 | 
			
		||||
        if ( address->host == INADDR_NONE ) {
 | 
			
		||||
            struct hostent *hp;
 | 
			
		||||
 | 
			
		||||
            hp = gethostbyname(host);
 | 
			
		||||
            if ( hp ) {
 | 
			
		||||
                SDL_memcpy(&address->host,hp->h_addr,hp->h_length);
 | 
			
		||||
            } else {
 | 
			
		||||
                retval = -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    address->port = SDLNet_Read16(&port);
 | 
			
		||||
 | 
			
		||||
    /* Return the status */
 | 
			
		||||
    return(retval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Resolve an ip address to a host name in canonical form.
 | 
			
		||||
   If the ip couldn't be resolved, this function returns NULL,
 | 
			
		||||
   otherwise a pointer to a static buffer containing the hostname
 | 
			
		||||
   is returned.  Note that this function is not thread-safe.
 | 
			
		||||
*/
 | 
			
		||||
/* Written by Miguel Angel Blanch.
 | 
			
		||||
 * Main Programmer of Arianne RPG.
 | 
			
		||||
 * http://come.to/arianne_rpg
 | 
			
		||||
 */
 | 
			
		||||
const char *SDLNet_ResolveIP(const IPaddress *ip)
 | 
			
		||||
{
 | 
			
		||||
    struct hostent *hp;
 | 
			
		||||
    struct in_addr in;
 | 
			
		||||
 | 
			
		||||
    hp = gethostbyaddr((const char *)&ip->host, sizeof(ip->host), AF_INET);
 | 
			
		||||
    if ( hp != NULL ) {
 | 
			
		||||
        return hp->h_name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    in.s_addr = ip->host;
 | 
			
		||||
    return inet_ntoa(in);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int SDLNet_GetLocalAddresses(IPaddress *addresses, int maxcount)
 | 
			
		||||
{
 | 
			
		||||
    int count = 0;
 | 
			
		||||
#ifdef SIOCGIFCONF
 | 
			
		||||
/* Defined on Mac OS X */
 | 
			
		||||
#ifndef _SIZEOF_ADDR_IFREQ
 | 
			
		||||
#define _SIZEOF_ADDR_IFREQ sizeof
 | 
			
		||||
#endif
 | 
			
		||||
    SOCKET sock;
 | 
			
		||||
    struct ifconf conf;
 | 
			
		||||
    char data[4096];
 | 
			
		||||
    struct ifreq *ifr;
 | 
			
		||||
    struct sockaddr_in *sock_addr;
 | 
			
		||||
 | 
			
		||||
    sock = socket(AF_INET, SOCK_DGRAM, 0);
 | 
			
		||||
    if ( sock == INVALID_SOCKET ) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    conf.ifc_len = sizeof(data);
 | 
			
		||||
    conf.ifc_buf = (caddr_t) data;
 | 
			
		||||
    if ( ioctl(sock, SIOCGIFCONF, &conf) < 0 ) {
 | 
			
		||||
        closesocket(sock);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ifr = (struct ifreq*)data;
 | 
			
		||||
    while ((char*)ifr < data+conf.ifc_len) {
 | 
			
		||||
        if (ifr->ifr_addr.sa_family == AF_INET) {
 | 
			
		||||
            if (count < maxcount) {
 | 
			
		||||
                sock_addr = (struct sockaddr_in*)&ifr->ifr_addr;
 | 
			
		||||
                addresses[count].host = sock_addr->sin_addr.s_addr;
 | 
			
		||||
                addresses[count].port = sock_addr->sin_port;
 | 
			
		||||
            }
 | 
			
		||||
            ++count;
 | 
			
		||||
        }
 | 
			
		||||
        ifr = (struct ifreq*)((char*)ifr + _SIZEOF_ADDR_IFREQ(*ifr));
 | 
			
		||||
    }
 | 
			
		||||
    closesocket(sock);
 | 
			
		||||
#elif defined(__WIN32__)
 | 
			
		||||
    //PIP_ADAPTER_INFO pAdapterInfo;
 | 
			
		||||
    //PIP_ADAPTER_INFO pAdapter;
 | 
			
		||||
    //PIP_ADDR_STRING pAddress;
 | 
			
		||||
    //DWORD dwRetVal = 0;
 | 
			
		||||
    //ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
 | 
			
		||||
 | 
			
		||||
    //pAdapterInfo = (IP_ADAPTER_INFO *) SDL_malloc(sizeof (IP_ADAPTER_INFO));
 | 
			
		||||
    //if (pAdapterInfo == NULL) {
 | 
			
		||||
    //    return 0;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == ERROR_BUFFER_OVERFLOW) {
 | 
			
		||||
    //    pAdapterInfo = (IP_ADAPTER_INFO *) SDL_realloc(pAdapterInfo, ulOutBufLen);
 | 
			
		||||
    //    if (pAdapterInfo == NULL) {
 | 
			
		||||
    //        return 0;
 | 
			
		||||
    //    }
 | 
			
		||||
    //    dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //if (dwRetVal == NO_ERROR) {
 | 
			
		||||
    //    for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) {
 | 
			
		||||
    //        for (pAddress = &pAdapter->IpAddressList; pAddress; pAddress = pAddress->Next) {
 | 
			
		||||
    //            if (count < maxcount) {
 | 
			
		||||
    //                addresses[count].host = inet_addr(pAddress->IpAddress.String);
 | 
			
		||||
    //                addresses[count].port = 0;
 | 
			
		||||
    //            }
 | 
			
		||||
    //            ++count;
 | 
			
		||||
    //        }
 | 
			
		||||
    //    }
 | 
			
		||||
    //}
 | 
			
		||||
    //SDL_free(pAdapterInfo);
 | 
			
		||||
#endif
 | 
			
		||||
    return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED /* function versions for binary compatibility */
 | 
			
		||||
 | 
			
		||||
#undef SDLNet_Write16
 | 
			
		||||
#undef SDLNet_Write32
 | 
			
		||||
#undef SDLNet_Read16
 | 
			
		||||
#undef SDLNet_Read32
 | 
			
		||||
 | 
			
		||||
/* Write a 16/32 bit value to network packet buffer */
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_Write16(Uint16 value, void *area);
 | 
			
		||||
extern DECLSPEC void SDLCALL SDLNet_Write32(Uint32 value, void *area);
 | 
			
		||||
 | 
			
		||||
/* Read a 16/32 bit value from network packet buffer */
 | 
			
		||||
extern DECLSPEC Uint16 SDLCALL SDLNet_Read16(void *area);
 | 
			
		||||
extern DECLSPEC Uint32 SDLCALL SDLNet_Read32(const void *area);
 | 
			
		||||
 | 
			
		||||
void  SDLNet_Write16(Uint16 value, void *areap)
 | 
			
		||||
{
 | 
			
		||||
    (*(Uint16 *)(areap) = SDL_SwapBE16(value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void   SDLNet_Write32(Uint32 value, void *areap)
 | 
			
		||||
{
 | 
			
		||||
    *(Uint32 *)(areap) = SDL_SwapBE32(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Uint16 SDLNet_Read16(void *areap)
 | 
			
		||||
{
 | 
			
		||||
    return (SDL_SwapBE16(*(Uint16 *)(areap)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Uint32 SDLNet_Read32(const void *areap)
 | 
			
		||||
{
 | 
			
		||||
    return (SDL_SwapBE32(*(Uint32 *)(areap)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED */
 | 
			
		||||
							
								
								
									
										297
									
								
								src/ext/net/SDLnetTCP.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								src/ext/net/SDLnetTCP.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,297 @@
 | 
			
		||||
/*
 | 
			
		||||
  SDL_net:  An example cross-platform network library for use with SDL
 | 
			
		||||
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
 | 
			
		||||
 | 
			
		||||
  This software is provided 'as-is', without any express or implied
 | 
			
		||||
  warranty.  In no event will the authors be held liable for any damages
 | 
			
		||||
  arising from the use of this software.
 | 
			
		||||
 | 
			
		||||
  Permission is granted to anyone to use this software for any purpose,
 | 
			
		||||
  including commercial applications, and to alter it and redistribute it
 | 
			
		||||
  freely, subject to the following restrictions:
 | 
			
		||||
 | 
			
		||||
  1. The origin of this software must not be misrepresented; you must not
 | 
			
		||||
     claim that you wrote the original software. If you use this software
 | 
			
		||||
     in a product, an acknowledgment in the product documentation would be
 | 
			
		||||
     appreciated but is not required.
 | 
			
		||||
  2. Altered source versions must be plainly marked as such, and must not be
 | 
			
		||||
     misrepresented as being the original software.
 | 
			
		||||
  3. This notice may not be removed or altered from any source distribution.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* $Id$ */
 | 
			
		||||
 | 
			
		||||
#include "SDLnetsys.h"
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
 | 
			
		||||
/* The network API for TCP sockets */
 | 
			
		||||
 | 
			
		||||
/* Since the UNIX/Win32/BeOS code is so different from MacOS,
 | 
			
		||||
   we'll just have two completely different sections here.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
struct _TCPsocket {
 | 
			
		||||
    int ready;
 | 
			
		||||
    SOCKET channel;
 | 
			
		||||
    IPaddress remoteAddress;
 | 
			
		||||
    IPaddress localAddress;
 | 
			
		||||
    int sflag;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Open a TCP network socket
 | 
			
		||||
   If 'remote' is NULL, this creates a local server socket on the given port,
 | 
			
		||||
   otherwise a TCP connection to the remote host and port is attempted.
 | 
			
		||||
   The newly created socket is returned, or NULL if there was an error.
 | 
			
		||||
*/
 | 
			
		||||
TCPsocket SDLNet_TCP_Open(IPaddress *ip)
 | 
			
		||||
{
 | 
			
		||||
    TCPsocket sock;
 | 
			
		||||
    struct sockaddr_in sock_addr;
 | 
			
		||||
 | 
			
		||||
    /* Allocate a TCP socket structure */
 | 
			
		||||
    sock = (TCPsocket)SDL_malloc(sizeof(*sock));
 | 
			
		||||
    if ( sock == NULL ) {
 | 
			
		||||
        SDLNet_SetError("Out of memory");
 | 
			
		||||
        goto error_return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Open the socket */
 | 
			
		||||
    sock->channel = socket(AF_INET, SOCK_STREAM, 0);
 | 
			
		||||
    if ( sock->channel == INVALID_SOCKET ) {
 | 
			
		||||
        SDLNet_SetError("Couldn't create socket");
 | 
			
		||||
        goto error_return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Connect to remote, or bind locally, as appropriate */
 | 
			
		||||
    if ( (ip->host != INADDR_NONE) && (ip->host != INADDR_ANY) ) {
 | 
			
		||||
 | 
			
		||||
    // #########  Connecting to remote
 | 
			
		||||
 | 
			
		||||
        SDL_memset(&sock_addr, 0, sizeof(sock_addr));
 | 
			
		||||
        sock_addr.sin_family = AF_INET;
 | 
			
		||||
        sock_addr.sin_addr.s_addr = ip->host;
 | 
			
		||||
        sock_addr.sin_port = ip->port;
 | 
			
		||||
 | 
			
		||||
        /* Connect to the remote host */
 | 
			
		||||
        if ( connect(sock->channel, (struct sockaddr *)&sock_addr,
 | 
			
		||||
                sizeof(sock_addr)) == SOCKET_ERROR ) {
 | 
			
		||||
            SDLNet_SetError("Couldn't connect to remote host");
 | 
			
		||||
            goto error_return;
 | 
			
		||||
        }
 | 
			
		||||
        sock->sflag = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
 | 
			
		||||
    // ##########  Binding locally
 | 
			
		||||
 | 
			
		||||
        SDL_memset(&sock_addr, 0, sizeof(sock_addr));
 | 
			
		||||
        sock_addr.sin_family = AF_INET;
 | 
			
		||||
        sock_addr.sin_addr.s_addr = INADDR_ANY;
 | 
			
		||||
        sock_addr.sin_port = ip->port;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Windows gets bad mojo with SO_REUSEADDR:
 | 
			
		||||
 * http://www.devolution.com/pipermail/sdl/2005-September/070491.html
 | 
			
		||||
 *   --ryan.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
        /* allow local address reuse */
 | 
			
		||||
        { int yes = 1;
 | 
			
		||||
            setsockopt(sock->channel, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        /* Bind the socket for listening */
 | 
			
		||||
        if ( bind(sock->channel, (struct sockaddr *)&sock_addr,
 | 
			
		||||
                sizeof(sock_addr)) == SOCKET_ERROR ) {
 | 
			
		||||
            SDLNet_SetError("Couldn't bind to local port");
 | 
			
		||||
            goto error_return;
 | 
			
		||||
        }
 | 
			
		||||
        if ( listen(sock->channel, 5) == SOCKET_ERROR ) {
 | 
			
		||||
            SDLNet_SetError("Couldn't listen to local port");
 | 
			
		||||
            goto error_return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Set the socket to non-blocking mode for accept() */
 | 
			
		||||
#if defined(__BEOS__) && defined(SO_NONBLOCK)
 | 
			
		||||
        /* On BeOS r5 there is O_NONBLOCK but it's for files only */
 | 
			
		||||
        {
 | 
			
		||||
            long b = 1;
 | 
			
		||||
            setsockopt(sock->channel, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
 | 
			
		||||
        }
 | 
			
		||||
#elif defined(O_NONBLOCK)
 | 
			
		||||
        {
 | 
			
		||||
            fcntl(sock->channel, F_SETFL, O_NONBLOCK);
 | 
			
		||||
        }
 | 
			
		||||
#elif defined(WIN32)
 | 
			
		||||
        {
 | 
			
		||||
            unsigned long mode = 1;
 | 
			
		||||
            ioctlsocket (sock->channel, FIONBIO, &mode);
 | 
			
		||||
        }
 | 
			
		||||
#elif defined(__OS2__)
 | 
			
		||||
        {
 | 
			
		||||
            int dontblock = 1;
 | 
			
		||||
            ioctl(sock->channel, FIONBIO, &dontblock);
 | 
			
		||||
        }
 | 
			
		||||
#else
 | 
			
		||||
#warning How do we set non-blocking mode on other operating systems?
 | 
			
		||||
#endif
 | 
			
		||||
        sock->sflag = 1;
 | 
			
		||||
    }
 | 
			
		||||
    sock->ready = 0;
 | 
			
		||||
 | 
			
		||||
#ifdef TCP_NODELAY
 | 
			
		||||
    /* Set the nodelay TCP option for real-time games */
 | 
			
		||||
    { int yes = 1;
 | 
			
		||||
    setsockopt(sock->channel, IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(yes));
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
#warning Building without TCP_NODELAY
 | 
			
		||||
#endif /* TCP_NODELAY */
 | 
			
		||||
 | 
			
		||||
    /* Fill in the channel host address */
 | 
			
		||||
    sock->remoteAddress.host = sock_addr.sin_addr.s_addr;
 | 
			
		||||
    sock->remoteAddress.port = sock_addr.sin_port;
 | 
			
		||||
 | 
			
		||||
    /* The socket is ready */
 | 
			
		||||
    return(sock);
 | 
			
		||||
 | 
			
		||||
error_return:
 | 
			
		||||
    SDLNet_TCP_Close(sock);
 | 
			
		||||
    return(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Accept an incoming connection on the given server socket.
 | 
			
		||||
   The newly created socket is returned, or NULL if there was an error.
 | 
			
		||||
*/
 | 
			
		||||
TCPsocket SDLNet_TCP_Accept(TCPsocket server)
 | 
			
		||||
{
 | 
			
		||||
    TCPsocket sock;
 | 
			
		||||
    struct sockaddr_in sock_addr;
 | 
			
		||||
    socklen_t sock_alen;
 | 
			
		||||
 | 
			
		||||
    /* Only server sockets can accept */
 | 
			
		||||
    if ( ! server->sflag ) {
 | 
			
		||||
        SDLNet_SetError("Only server sockets can accept()");
 | 
			
		||||
        return(NULL);
 | 
			
		||||
    }
 | 
			
		||||
    server->ready = 0;
 | 
			
		||||
 | 
			
		||||
    /* Allocate a TCP socket structure */
 | 
			
		||||
    sock = (TCPsocket)SDL_malloc(sizeof(*sock));
 | 
			
		||||
    if ( sock == NULL ) {
 | 
			
		||||
        SDLNet_SetError("Out of memory");
 | 
			
		||||
        goto error_return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Accept a new TCP connection on a server socket */
 | 
			
		||||
    sock_alen = sizeof(sock_addr);
 | 
			
		||||
    sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr,
 | 
			
		||||
                                &sock_alen);
 | 
			
		||||
    if ( sock->channel == INVALID_SOCKET ) {
 | 
			
		||||
        SDLNet_SetError("accept() failed");
 | 
			
		||||
        goto error_return;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef WIN32
 | 
			
		||||
    {
 | 
			
		||||
        /* passing a zero value, socket mode set to block on */
 | 
			
		||||
        unsigned long mode = 0;
 | 
			
		||||
        ioctlsocket (sock->channel, FIONBIO, &mode);
 | 
			
		||||
    }
 | 
			
		||||
#elif defined(O_NONBLOCK)
 | 
			
		||||
    {
 | 
			
		||||
        int flags = fcntl(sock->channel, F_GETFL, 0);
 | 
			
		||||
        fcntl(sock->channel, F_SETFL, flags & ~O_NONBLOCK);
 | 
			
		||||
    }
 | 
			
		||||
#endif /* WIN32 */
 | 
			
		||||
    sock->remoteAddress.host = sock_addr.sin_addr.s_addr;
 | 
			
		||||
    sock->remoteAddress.port = sock_addr.sin_port;
 | 
			
		||||
 | 
			
		||||
    sock->sflag = 0;
 | 
			
		||||
    sock->ready = 0;
 | 
			
		||||
 | 
			
		||||
    /* The socket is ready */
 | 
			
		||||
    return(sock);
 | 
			
		||||
 | 
			
		||||
error_return:
 | 
			
		||||
    SDLNet_TCP_Close(sock);
 | 
			
		||||
    return(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get the IP address of the remote system associated with the socket.
 | 
			
		||||
   If the socket is a server socket, this function returns NULL.
 | 
			
		||||
*/
 | 
			
		||||
IPaddress *SDLNet_TCP_GetPeerAddress(TCPsocket sock)
 | 
			
		||||
{
 | 
			
		||||
    if ( sock->sflag ) {
 | 
			
		||||
        return(NULL);
 | 
			
		||||
    }
 | 
			
		||||
    return(&sock->remoteAddress);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send 'len' bytes of 'data' over the non-server socket 'sock'
 | 
			
		||||
   This function returns the actual amount of data sent.  If the return value
 | 
			
		||||
   is less than the amount of data sent, then either the remote connection was
 | 
			
		||||
   closed, or an unknown socket error occurred.
 | 
			
		||||
*/
 | 
			
		||||
int SDLNet_TCP_Send(TCPsocket sock, const void *datap, int len)
 | 
			
		||||
{
 | 
			
		||||
    const Uint8 *data = (const Uint8 *)datap;   /* For pointer arithmetic */
 | 
			
		||||
    int sent, left;
 | 
			
		||||
 | 
			
		||||
    /* Server sockets are for accepting connections only */
 | 
			
		||||
    if ( sock->sflag ) {
 | 
			
		||||
        SDLNet_SetError("Server sockets cannot send");
 | 
			
		||||
        return(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Keep sending data until it's sent or an error occurs */
 | 
			
		||||
    left = len;
 | 
			
		||||
    sent = 0;
 | 
			
		||||
    SDLNet_SetLastError(0);
 | 
			
		||||
    do {
 | 
			
		||||
        len = send(sock->channel, (const char *) data, left, 0);
 | 
			
		||||
        if ( len > 0 ) {
 | 
			
		||||
            sent += len;
 | 
			
		||||
            left -= len;
 | 
			
		||||
            data += len;
 | 
			
		||||
        }
 | 
			
		||||
    } while ( (left > 0) && ((len > 0) || (SDLNet_GetLastError() == EINTR)) );
 | 
			
		||||
 | 
			
		||||
    return(sent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive up to 'maxlen' bytes of data over the non-server socket 'sock',
 | 
			
		||||
   and store them in the buffer pointed to by 'data'.
 | 
			
		||||
   This function returns the actual amount of data received.  If the return
 | 
			
		||||
   value is less than or equal to zero, then either the remote connection was
 | 
			
		||||
   closed, or an unknown socket error occurred.
 | 
			
		||||
*/
 | 
			
		||||
int SDLNet_TCP_Recv(TCPsocket sock, void *data, int maxlen)
 | 
			
		||||
{
 | 
			
		||||
    int len;
 | 
			
		||||
 | 
			
		||||
    /* Server sockets are for accepting connections only */
 | 
			
		||||
    if ( sock->sflag ) {
 | 
			
		||||
        SDLNet_SetError("Server sockets cannot receive");
 | 
			
		||||
        return(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SDLNet_SetLastError(0);
 | 
			
		||||
    do {
 | 
			
		||||
        len = recv(sock->channel, (char *) data, maxlen, 0);
 | 
			
		||||
    } while ( SDLNet_GetLastError() == EINTR );
 | 
			
		||||
 | 
			
		||||
    sock->ready = 0;
 | 
			
		||||
    return(len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Close a TCP network socket */
 | 
			
		||||
void SDLNet_TCP_Close(TCPsocket sock)
 | 
			
		||||
{
 | 
			
		||||
    if ( sock != NULL ) {
 | 
			
		||||
        if ( sock->channel != INVALID_SOCKET ) {
 | 
			
		||||
            closesocket(sock->channel);
 | 
			
		||||
        }
 | 
			
		||||
        SDL_free(sock);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										524
									
								
								src/ext/net/SDLnetUDP.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										524
									
								
								src/ext/net/SDLnetUDP.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,524 @@
 | 
			
		||||
/*
 | 
			
		||||
  SDL_net:  An example cross-platform network library for use with SDL
 | 
			
		||||
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
 | 
			
		||||
 | 
			
		||||
  This software is provided 'as-is', without any express or implied
 | 
			
		||||
  warranty.  In no event will the authors be held liable for any damages
 | 
			
		||||
  arising from the use of this software.
 | 
			
		||||
 | 
			
		||||
  Permission is granted to anyone to use this software for any purpose,
 | 
			
		||||
  including commercial applications, and to alter it and redistribute it
 | 
			
		||||
  freely, subject to the following restrictions:
 | 
			
		||||
 | 
			
		||||
  1. The origin of this software must not be misrepresented; you must not
 | 
			
		||||
     claim that you wrote the original software. If you use this software
 | 
			
		||||
     in a product, an acknowledgment in the product documentation would be
 | 
			
		||||
     appreciated but is not required.
 | 
			
		||||
  2. Altered source versions must be plainly marked as such, and must not be
 | 
			
		||||
     misrepresented as being the original software.
 | 
			
		||||
  3. This notice may not be removed or altered from any source distribution.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* $Id$ */
 | 
			
		||||
 | 
			
		||||
#include "SDLnetsys.h"
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __WIN32__
 | 
			
		||||
#define srandom srand
 | 
			
		||||
#define random  rand
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct UDP_channel {
 | 
			
		||||
    int numbound;
 | 
			
		||||
    IPaddress address[SDLNET_MAX_UDPADDRESSES];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _UDPsocket {
 | 
			
		||||
    int ready;
 | 
			
		||||
    SOCKET channel;
 | 
			
		||||
    IPaddress address;
 | 
			
		||||
 | 
			
		||||
    struct UDP_channel binding[SDLNET_MAX_UDPCHANNELS];
 | 
			
		||||
 | 
			
		||||
    /* For debugging purposes */
 | 
			
		||||
    int packetloss;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Allocate/free a single UDP packet 'size' bytes long.
 | 
			
		||||
   The new packet is returned, or NULL if the function ran out of memory.
 | 
			
		||||
 */
 | 
			
		||||
extern UDPpacket *SDLNet_AllocPacket(int size)
 | 
			
		||||
{
 | 
			
		||||
    UDPpacket *packet;
 | 
			
		||||
    int error;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    error = 1;
 | 
			
		||||
    packet = (UDPpacket *)SDL_malloc(sizeof(*packet));
 | 
			
		||||
    if ( packet != NULL ) {
 | 
			
		||||
        packet->maxlen = size;
 | 
			
		||||
        packet->data = (Uint8 *)SDL_malloc(size);
 | 
			
		||||
        if ( packet->data != NULL ) {
 | 
			
		||||
            error = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if ( error ) {
 | 
			
		||||
        SDLNet_SetError("Out of memory");
 | 
			
		||||
        SDLNet_FreePacket(packet);
 | 
			
		||||
        packet = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return(packet);
 | 
			
		||||
}
 | 
			
		||||
int SDLNet_ResizePacket(UDPpacket *packet, int newsize)
 | 
			
		||||
{
 | 
			
		||||
    Uint8 *newdata;
 | 
			
		||||
 | 
			
		||||
    newdata = (Uint8 *)SDL_malloc(newsize);
 | 
			
		||||
    if ( newdata != NULL ) {
 | 
			
		||||
        SDL_free(packet->data);
 | 
			
		||||
        packet->data = newdata;
 | 
			
		||||
        packet->maxlen = newsize;
 | 
			
		||||
    }
 | 
			
		||||
    return(packet->maxlen);
 | 
			
		||||
}
 | 
			
		||||
extern void SDLNet_FreePacket(UDPpacket *packet)
 | 
			
		||||
{
 | 
			
		||||
    if ( packet ) {
 | 
			
		||||
        SDL_free(packet->data);
 | 
			
		||||
        SDL_free(packet);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets,
 | 
			
		||||
   each 'size' bytes long.
 | 
			
		||||
   A pointer to the packet array is returned, or NULL if the function ran out
 | 
			
		||||
   of memory.
 | 
			
		||||
 */
 | 
			
		||||
UDPpacket **SDLNet_AllocPacketV(int howmany, int size)
 | 
			
		||||
{
 | 
			
		||||
    UDPpacket **packetV;
 | 
			
		||||
 | 
			
		||||
    packetV = (UDPpacket **)SDL_malloc((howmany+1)*sizeof(*packetV));
 | 
			
		||||
    if ( packetV != NULL ) {
 | 
			
		||||
        int i;
 | 
			
		||||
        for ( i=0; i<howmany; ++i ) {
 | 
			
		||||
            packetV[i] = SDLNet_AllocPacket(size);
 | 
			
		||||
            if ( packetV[i] == NULL ) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        packetV[i] = NULL;
 | 
			
		||||
 | 
			
		||||
        if ( i != howmany ) {
 | 
			
		||||
            SDLNet_SetError("Out of memory");
 | 
			
		||||
            SDLNet_FreePacketV(packetV);
 | 
			
		||||
            packetV = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return(packetV);
 | 
			
		||||
}
 | 
			
		||||
void SDLNet_FreePacketV(UDPpacket **packetV)
 | 
			
		||||
{
 | 
			
		||||
    if ( packetV ) {
 | 
			
		||||
        int i;
 | 
			
		||||
        for ( i=0; packetV[i]; ++i ) {
 | 
			
		||||
            SDLNet_FreePacket(packetV[i]);
 | 
			
		||||
        }
 | 
			
		||||
        SDL_free(packetV);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Since the UNIX/Win32/BeOS code is so different from MacOS,
 | 
			
		||||
   we'll just have two completely different sections here.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* Open a UDP network socket
 | 
			
		||||
   If 'port' is non-zero, the UDP socket is bound to a fixed local port.
 | 
			
		||||
*/
 | 
			
		||||
UDPsocket SDLNet_UDP_Open(Uint16 port)
 | 
			
		||||
{
 | 
			
		||||
    UDPsocket sock;
 | 
			
		||||
    struct sockaddr_in sock_addr;
 | 
			
		||||
    socklen_t sock_len;
 | 
			
		||||
 | 
			
		||||
    /* Allocate a UDP socket structure */
 | 
			
		||||
    sock = (UDPsocket)SDL_malloc(sizeof(*sock));
 | 
			
		||||
    if ( sock == NULL ) {
 | 
			
		||||
        SDLNet_SetError("Out of memory");
 | 
			
		||||
        goto error_return;
 | 
			
		||||
    }
 | 
			
		||||
    SDL_memset(sock, 0, sizeof(*sock));
 | 
			
		||||
    SDL_memset(&sock_addr, 0, sizeof(sock_addr));
 | 
			
		||||
 | 
			
		||||
    /* Open the socket */
 | 
			
		||||
    sock->channel = socket(AF_INET, SOCK_DGRAM, 0);
 | 
			
		||||
    if ( sock->channel == INVALID_SOCKET )
 | 
			
		||||
    {
 | 
			
		||||
        SDLNet_SetError("Couldn't create socket");
 | 
			
		||||
        goto error_return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Bind locally, if appropriate */
 | 
			
		||||
    sock_addr.sin_family = AF_INET;
 | 
			
		||||
    sock_addr.sin_addr.s_addr = INADDR_ANY;
 | 
			
		||||
    sock_addr.sin_port = SDLNet_Read16(&port);
 | 
			
		||||
 | 
			
		||||
    /* Bind the socket for listening */
 | 
			
		||||
    if ( bind(sock->channel, (struct sockaddr *)&sock_addr,
 | 
			
		||||
            sizeof(sock_addr)) == SOCKET_ERROR ) {
 | 
			
		||||
        SDLNet_SetError("Couldn't bind to local port");
 | 
			
		||||
        goto error_return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Get the bound address and port */
 | 
			
		||||
    sock_len = sizeof(sock_addr);
 | 
			
		||||
    if ( getsockname(sock->channel, (struct sockaddr *)&sock_addr, &sock_len) < 0 ) {
 | 
			
		||||
        SDLNet_SetError("Couldn't get socket address");
 | 
			
		||||
        goto error_return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Fill in the channel host address */
 | 
			
		||||
    sock->address.host = sock_addr.sin_addr.s_addr;
 | 
			
		||||
    sock->address.port = sock_addr.sin_port;
 | 
			
		||||
 | 
			
		||||
#ifdef SO_BROADCAST
 | 
			
		||||
    /* Allow LAN broadcasts with the socket */
 | 
			
		||||
    { int yes = 1;
 | 
			
		||||
        setsockopt(sock->channel, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef IP_ADD_MEMBERSHIP
 | 
			
		||||
    /* Receive LAN multicast packets on 224.0.0.1
 | 
			
		||||
       This automatically works on Mac OS X, Linux and BSD, but needs
 | 
			
		||||
       this code on Windows.
 | 
			
		||||
    */
 | 
			
		||||
    /* A good description of multicast can be found here:
 | 
			
		||||
        http://www.docs.hp.com/en/B2355-90136/ch05s05.html
 | 
			
		||||
    */
 | 
			
		||||
    /* FIXME: Add support for joining arbitrary groups to the API */
 | 
			
		||||
    {
 | 
			
		||||
        struct ip_mreq  g;
 | 
			
		||||
 | 
			
		||||
        g.imr_multiaddr.s_addr = inet_addr("224.0.0.1");
 | 
			
		||||
        g.imr_interface.s_addr = INADDR_ANY;
 | 
			
		||||
        setsockopt(sock->channel, IPPROTO_IP, IP_ADD_MEMBERSHIP,
 | 
			
		||||
               (char*)&g, sizeof(g));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* The socket is ready */
 | 
			
		||||
 | 
			
		||||
    return(sock);
 | 
			
		||||
 | 
			
		||||
error_return:
 | 
			
		||||
    SDLNet_UDP_Close(sock);
 | 
			
		||||
 | 
			
		||||
    return(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SDLNet_UDP_SetPacketLoss(UDPsocket sock, int percent)
 | 
			
		||||
{
 | 
			
		||||
    /* FIXME: We may want this behavior to be reproducible
 | 
			
		||||
          but there isn't a portable reentrant random
 | 
			
		||||
          number generator with good randomness.
 | 
			
		||||
    */
 | 
			
		||||
    srandom(time(NULL));
 | 
			
		||||
 | 
			
		||||
    if (percent < 0) {
 | 
			
		||||
        percent = 0;
 | 
			
		||||
    } else if (percent > 100) {
 | 
			
		||||
        percent = 100;
 | 
			
		||||
    }
 | 
			
		||||
    sock->packetloss = percent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Verify that the channel is in the valid range */
 | 
			
		||||
static int ValidChannel(int channel)
 | 
			
		||||
{
 | 
			
		||||
    if ( (channel < 0) || (channel >= SDLNET_MAX_UDPCHANNELS) ) {
 | 
			
		||||
        SDLNet_SetError("Invalid channel");
 | 
			
		||||
        return(0);
 | 
			
		||||
    }
 | 
			
		||||
    return(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Bind the address 'address' to the requested channel on the UDP socket.
 | 
			
		||||
   If the channel is -1, then the first unbound channel that has not yet
 | 
			
		||||
   been bound to the maximum number of addresses will be bound with
 | 
			
		||||
   the given address as it's primary address.
 | 
			
		||||
   If the channel is already bound, this new address will be added to the
 | 
			
		||||
   list of valid source addresses for packets arriving on the channel.
 | 
			
		||||
   If the channel is not already bound, then the address becomes the primary
 | 
			
		||||
   address, to which all outbound packets on the channel are sent.
 | 
			
		||||
   This function returns the channel which was bound, or -1 on error.
 | 
			
		||||
*/
 | 
			
		||||
int SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address)
 | 
			
		||||
{
 | 
			
		||||
    struct UDP_channel *binding;
 | 
			
		||||
 | 
			
		||||
    if ( sock == NULL ) {
 | 
			
		||||
        SDLNet_SetError("Passed a NULL socket");
 | 
			
		||||
        return(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( channel == -1 ) {
 | 
			
		||||
        for ( channel=0; channel < SDLNET_MAX_UDPCHANNELS; ++channel ) {
 | 
			
		||||
            binding = &sock->binding[channel];
 | 
			
		||||
            if ( binding->numbound < SDLNET_MAX_UDPADDRESSES ) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if ( ! ValidChannel(channel) ) {
 | 
			
		||||
            return(-1);
 | 
			
		||||
        }
 | 
			
		||||
        binding = &sock->binding[channel];
 | 
			
		||||
    }
 | 
			
		||||
    if ( binding->numbound == SDLNET_MAX_UDPADDRESSES ) {
 | 
			
		||||
        SDLNet_SetError("No room for new addresses");
 | 
			
		||||
        return(-1);
 | 
			
		||||
    }
 | 
			
		||||
    binding->address[binding->numbound++] = *address;
 | 
			
		||||
    return(channel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Unbind all addresses from the given channel */
 | 
			
		||||
void SDLNet_UDP_Unbind(UDPsocket sock, int channel)
 | 
			
		||||
{
 | 
			
		||||
    if ( (channel >= 0) && (channel < SDLNET_MAX_UDPCHANNELS) ) {
 | 
			
		||||
        sock->binding[channel].numbound = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get the primary IP address of the remote system associated with the
 | 
			
		||||
   socket and channel.
 | 
			
		||||
   If the channel is not bound, this function returns NULL.
 | 
			
		||||
 */
 | 
			
		||||
IPaddress *SDLNet_UDP_GetPeerAddress(UDPsocket sock, int channel)
 | 
			
		||||
{
 | 
			
		||||
    IPaddress *address;
 | 
			
		||||
 | 
			
		||||
    address = NULL;
 | 
			
		||||
    switch (channel) {
 | 
			
		||||
        case -1:
 | 
			
		||||
            /* Return the actual address of the socket */
 | 
			
		||||
            address = &sock->address;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            /* Return the address of the bound channel */
 | 
			
		||||
            if ( ValidChannel(channel) &&
 | 
			
		||||
                (sock->binding[channel].numbound > 0) ) {
 | 
			
		||||
                address = &sock->binding[channel].address[0];
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    return(address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send a vector of packets to the the channels specified within the packet.
 | 
			
		||||
   If the channel specified in the packet is -1, the packet will be sent to
 | 
			
		||||
   the address in the 'src' member of the packet.
 | 
			
		||||
   Each packet will be updated with the status of the packet after it has
 | 
			
		||||
   been sent, -1 if the packet send failed.
 | 
			
		||||
   This function returns the number of packets sent.
 | 
			
		||||
*/
 | 
			
		||||
int SDLNet_UDP_SendV(UDPsocket sock, UDPpacket **packets, int npackets)
 | 
			
		||||
{
 | 
			
		||||
    int numsent, i, j;
 | 
			
		||||
    struct UDP_channel *binding;
 | 
			
		||||
    int status;
 | 
			
		||||
    int sock_len;
 | 
			
		||||
    struct sockaddr_in sock_addr;
 | 
			
		||||
 | 
			
		||||
    if ( sock == NULL ) {
 | 
			
		||||
        SDLNet_SetError("Passed a NULL socket");
 | 
			
		||||
        return(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set up the variables to send packets */
 | 
			
		||||
    sock_len = sizeof(sock_addr);
 | 
			
		||||
 | 
			
		||||
    numsent = 0;
 | 
			
		||||
    for ( i=0; i<npackets; ++i )
 | 
			
		||||
    {
 | 
			
		||||
        /* Simulate packet loss, if desired */
 | 
			
		||||
        if (sock->packetloss) {
 | 
			
		||||
            if ((random()%100) <= sock->packetloss) {
 | 
			
		||||
                packets[i]->status = packets[i]->len;
 | 
			
		||||
                ++numsent;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* if channel is < 0, then use channel specified in sock */
 | 
			
		||||
 | 
			
		||||
        if ( packets[i]->channel < 0 )
 | 
			
		||||
        {
 | 
			
		||||
            sock_addr.sin_addr.s_addr = packets[i]->address.host;
 | 
			
		||||
            sock_addr.sin_port = packets[i]->address.port;
 | 
			
		||||
            sock_addr.sin_family = AF_INET;
 | 
			
		||||
            status = sendto(sock->channel,
 | 
			
		||||
                    packets[i]->data, packets[i]->len, 0,
 | 
			
		||||
                    (struct sockaddr *)&sock_addr,sock_len);
 | 
			
		||||
            if ( status >= 0 )
 | 
			
		||||
            {
 | 
			
		||||
                packets[i]->status = status;
 | 
			
		||||
                ++numsent;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            /* Send to each of the bound addresses on the channel */
 | 
			
		||||
#ifdef DEBUG_NET
 | 
			
		||||
            printf("SDLNet_UDP_SendV sending packet to channel = %d\n", packets[i]->channel );
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
            binding = &sock->binding[packets[i]->channel];
 | 
			
		||||
 | 
			
		||||
            for ( j=binding->numbound-1; j>=0; --j )
 | 
			
		||||
            {
 | 
			
		||||
                sock_addr.sin_addr.s_addr = binding->address[j].host;
 | 
			
		||||
                sock_addr.sin_port = binding->address[j].port;
 | 
			
		||||
                sock_addr.sin_family = AF_INET;
 | 
			
		||||
                status = sendto(sock->channel,
 | 
			
		||||
                        packets[i]->data, packets[i]->len, 0,
 | 
			
		||||
                        (struct sockaddr *)&sock_addr,sock_len);
 | 
			
		||||
                if ( status >= 0 )
 | 
			
		||||
                {
 | 
			
		||||
                    packets[i]->status = status;
 | 
			
		||||
                    ++numsent;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return(numsent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet)
 | 
			
		||||
{
 | 
			
		||||
    /* This is silly, but... */
 | 
			
		||||
    packet->channel = channel;
 | 
			
		||||
    return(SDLNet_UDP_SendV(sock, &packet, 1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns true if a socket is has data available for reading right now */
 | 
			
		||||
static int SocketReady(SOCKET sock)
 | 
			
		||||
{
 | 
			
		||||
    int retval = 0;
 | 
			
		||||
    struct timeval tv;
 | 
			
		||||
    fd_set mask;
 | 
			
		||||
 | 
			
		||||
    /* Check the file descriptors for available data */
 | 
			
		||||
    do {
 | 
			
		||||
        SDLNet_SetLastError(0);
 | 
			
		||||
 | 
			
		||||
        /* Set up the mask of file descriptors */
 | 
			
		||||
        FD_ZERO(&mask);
 | 
			
		||||
        FD_SET(sock, &mask);
 | 
			
		||||
 | 
			
		||||
        /* Set up the timeout */
 | 
			
		||||
        tv.tv_sec = 0;
 | 
			
		||||
        tv.tv_usec = 0;
 | 
			
		||||
 | 
			
		||||
        /* Look! */
 | 
			
		||||
        retval = select(sock+1, &mask, NULL, NULL, &tv);
 | 
			
		||||
    } while ( SDLNet_GetLastError() == EINTR );
 | 
			
		||||
 | 
			
		||||
    return(retval == 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive a vector of pending packets from the UDP socket.
 | 
			
		||||
   The returned packets contain the source address and the channel they arrived
 | 
			
		||||
   on.  If they did not arrive on a bound channel, the the channel will be set
 | 
			
		||||
   to -1.
 | 
			
		||||
   This function returns the number of packets read from the network, or -1
 | 
			
		||||
   on error.  This function does not block, so can return 0 packets pending.
 | 
			
		||||
*/
 | 
			
		||||
extern int SDLNet_UDP_RecvV(UDPsocket sock, UDPpacket **packets)
 | 
			
		||||
{
 | 
			
		||||
    int numrecv, i, j;
 | 
			
		||||
    struct UDP_channel *binding;
 | 
			
		||||
    socklen_t sock_len;
 | 
			
		||||
    struct sockaddr_in sock_addr;
 | 
			
		||||
 | 
			
		||||
    if ( sock == NULL ) {
 | 
			
		||||
        return(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    numrecv = 0;
 | 
			
		||||
    while ( packets[numrecv] && SocketReady(sock->channel) )
 | 
			
		||||
    {
 | 
			
		||||
        UDPpacket *packet;
 | 
			
		||||
 | 
			
		||||
        packet = packets[numrecv];
 | 
			
		||||
 | 
			
		||||
        sock_len = sizeof(sock_addr);
 | 
			
		||||
        packet->status = recvfrom(sock->channel,
 | 
			
		||||
                packet->data, packet->maxlen, 0,
 | 
			
		||||
                (struct sockaddr *)&sock_addr,
 | 
			
		||||
                        &sock_len);
 | 
			
		||||
        if ( packet->status >= 0 ) {
 | 
			
		||||
            packet->len = packet->status;
 | 
			
		||||
            packet->address.host = sock_addr.sin_addr.s_addr;
 | 
			
		||||
            packet->address.port = sock_addr.sin_port;
 | 
			
		||||
            packet->channel = -1;
 | 
			
		||||
 | 
			
		||||
            for (i=(SDLNET_MAX_UDPCHANNELS-1); i>=0; --i )
 | 
			
		||||
            {
 | 
			
		||||
                binding = &sock->binding[i];
 | 
			
		||||
 | 
			
		||||
                for ( j=binding->numbound-1; j>=0; --j )
 | 
			
		||||
                {
 | 
			
		||||
                    if ( (packet->address.host == binding->address[j].host) &&
 | 
			
		||||
                         (packet->address.port == binding->address[j].port) )
 | 
			
		||||
                    {
 | 
			
		||||
                        packet->channel = i;
 | 
			
		||||
                        goto foundit; /* break twice */
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
foundit:
 | 
			
		||||
            ++numrecv;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            packet->len = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sock->ready = 0;
 | 
			
		||||
 | 
			
		||||
    return(numrecv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive a single packet from the UDP socket.
 | 
			
		||||
   The returned packet contains the source address and the channel it arrived
 | 
			
		||||
   on.  If it did not arrive on a bound channel, the the channel will be set
 | 
			
		||||
   to -1.
 | 
			
		||||
   This function returns the number of packets read from the network, or -1
 | 
			
		||||
   on error.  This function does not block, so can return 0 packets pending.
 | 
			
		||||
*/
 | 
			
		||||
int SDLNet_UDP_Recv(UDPsocket sock, UDPpacket *packet)
 | 
			
		||||
{
 | 
			
		||||
    UDPpacket *packets[2];
 | 
			
		||||
 | 
			
		||||
    /* Receive a packet array of 1 */
 | 
			
		||||
    packets[0] = packet;
 | 
			
		||||
    packets[1] = NULL;
 | 
			
		||||
    return(SDLNet_UDP_RecvV(sock, packets));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Close a UDP network socket */
 | 
			
		||||
extern void SDLNet_UDP_Close(UDPsocket sock)
 | 
			
		||||
{
 | 
			
		||||
    if ( sock != NULL ) {
 | 
			
		||||
        if ( sock->channel != INVALID_SOCKET ) {
 | 
			
		||||
            closesocket(sock->channel);
 | 
			
		||||
        }
 | 
			
		||||
        SDL_free(sock);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										163
									
								
								src/ext/net/SDLnetselect.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/ext/net/SDLnetselect.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
			
		||||
/*
 | 
			
		||||
  SDL_net:  An example cross-platform network library for use with SDL
 | 
			
		||||
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
 | 
			
		||||
 | 
			
		||||
  This software is provided 'as-is', without any express or implied
 | 
			
		||||
  warranty.  In no event will the authors be held liable for any damages
 | 
			
		||||
  arising from the use of this software.
 | 
			
		||||
 | 
			
		||||
  Permission is granted to anyone to use this software for any purpose,
 | 
			
		||||
  including commercial applications, and to alter it and redistribute it
 | 
			
		||||
  freely, subject to the following restrictions:
 | 
			
		||||
 | 
			
		||||
  1. The origin of this software must not be misrepresented; you must not
 | 
			
		||||
     claim that you wrote the original software. If you use this software
 | 
			
		||||
     in a product, an acknowledgment in the product documentation would be
 | 
			
		||||
     appreciated but is not required.
 | 
			
		||||
  2. Altered source versions must be plainly marked as such, and must not be
 | 
			
		||||
     misrepresented as being the original software.
 | 
			
		||||
  3. This notice may not be removed or altered from any source distribution.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* $Id$ */
 | 
			
		||||
 | 
			
		||||
#include "SDLnetsys.h"
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
 | 
			
		||||
/* The select() API for network sockets */
 | 
			
		||||
 | 
			
		||||
struct SDLNet_Socket {
 | 
			
		||||
    int ready;
 | 
			
		||||
    SOCKET channel;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _SDLNet_SocketSet {
 | 
			
		||||
    int numsockets;
 | 
			
		||||
    int maxsockets;
 | 
			
		||||
    struct SDLNet_Socket **sockets;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Allocate a socket set for use with SDLNet_CheckSockets()
 | 
			
		||||
   This returns a socket set for up to 'maxsockets' sockets, or NULL if
 | 
			
		||||
   the function ran out of memory.
 | 
			
		||||
 */
 | 
			
		||||
SDLNet_SocketSet SDLNet_AllocSocketSet(int maxsockets)
 | 
			
		||||
{
 | 
			
		||||
    struct _SDLNet_SocketSet *set;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    set = (struct _SDLNet_SocketSet *)SDL_malloc(sizeof(*set));
 | 
			
		||||
    if ( set != NULL ) {
 | 
			
		||||
        set->numsockets = 0;
 | 
			
		||||
        set->maxsockets = maxsockets;
 | 
			
		||||
        set->sockets = (struct SDLNet_Socket **)SDL_malloc
 | 
			
		||||
                    (maxsockets*sizeof(*set->sockets));
 | 
			
		||||
        if ( set->sockets != NULL ) {
 | 
			
		||||
            for ( i=0; i<maxsockets; ++i ) {
 | 
			
		||||
                set->sockets[i] = NULL;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            SDL_free(set);
 | 
			
		||||
            set = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return(set);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add a socket to a set of sockets to be checked for available data */
 | 
			
		||||
int SDLNet_AddSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock)
 | 
			
		||||
{
 | 
			
		||||
    if ( sock != NULL ) {
 | 
			
		||||
        if ( set->numsockets == set->maxsockets ) {
 | 
			
		||||
            SDLNet_SetError("socketset is full");
 | 
			
		||||
            return(-1);
 | 
			
		||||
        }
 | 
			
		||||
        set->sockets[set->numsockets++] = (struct SDLNet_Socket *)sock;
 | 
			
		||||
    }
 | 
			
		||||
    return(set->numsockets);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Remove a socket from a set of sockets to be checked for available data */
 | 
			
		||||
int SDLNet_DelSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    if ( sock != NULL ) {
 | 
			
		||||
        for ( i=0; i<set->numsockets; ++i ) {
 | 
			
		||||
            if ( set->sockets[i] == (struct SDLNet_Socket *)sock ) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ( i == set->numsockets ) {
 | 
			
		||||
            SDLNet_SetError("socket not found in socketset");
 | 
			
		||||
            return(-1);
 | 
			
		||||
        }
 | 
			
		||||
        --set->numsockets;
 | 
			
		||||
        for ( ; i<set->numsockets; ++i ) {
 | 
			
		||||
            set->sockets[i] = set->sockets[i+1];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return(set->numsockets);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function checks to see if data is available for reading on the
 | 
			
		||||
   given set of sockets.  If 'timeout' is 0, it performs a quick poll,
 | 
			
		||||
   otherwise the function returns when either data is available for
 | 
			
		||||
   reading, or the timeout in milliseconds has elapsed, which ever occurs
 | 
			
		||||
   first.  This function returns the number of sockets ready for reading,
 | 
			
		||||
   or -1 if there was an error with the select() system call.
 | 
			
		||||
*/
 | 
			
		||||
int SDLNet_CheckSockets(SDLNet_SocketSet set, Uint32 timeout)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    SOCKET maxfd;
 | 
			
		||||
    int retval;
 | 
			
		||||
    struct timeval tv;
 | 
			
		||||
    fd_set mask;
 | 
			
		||||
 | 
			
		||||
    /* Find the largest file descriptor */
 | 
			
		||||
    maxfd = 0;
 | 
			
		||||
    for ( i=set->numsockets-1; i>=0; --i ) {
 | 
			
		||||
        if ( set->sockets[i]->channel > maxfd ) {
 | 
			
		||||
            maxfd = set->sockets[i]->channel;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Check the file descriptors for available data */
 | 
			
		||||
    do {
 | 
			
		||||
        SDLNet_SetLastError(0);
 | 
			
		||||
 | 
			
		||||
        /* Set up the mask of file descriptors */
 | 
			
		||||
        FD_ZERO(&mask);
 | 
			
		||||
        for ( i=set->numsockets-1; i>=0; --i ) {
 | 
			
		||||
            FD_SET(set->sockets[i]->channel, &mask);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Set up the timeout */
 | 
			
		||||
        tv.tv_sec = timeout/1000;
 | 
			
		||||
        tv.tv_usec = (timeout%1000)*1000;
 | 
			
		||||
 | 
			
		||||
        /* Look! */
 | 
			
		||||
        retval = select(maxfd+1, &mask, NULL, NULL, &tv);
 | 
			
		||||
    } while ( SDLNet_GetLastError() == EINTR );
 | 
			
		||||
 | 
			
		||||
    /* Mark all file descriptors ready that have data available */
 | 
			
		||||
    if ( retval > 0 ) {
 | 
			
		||||
        for ( i=set->numsockets-1; i>=0; --i ) {
 | 
			
		||||
            if ( FD_ISSET(set->sockets[i]->channel, &mask) ) {
 | 
			
		||||
                set->sockets[i]->ready = 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return(retval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Free a set of sockets allocated by SDL_NetAllocSocketSet() */
 | 
			
		||||
extern void SDLNet_FreeSocketSet(SDLNet_SocketSet set)
 | 
			
		||||
{
 | 
			
		||||
    if ( set ) {
 | 
			
		||||
        SDL_free(set->sockets);
 | 
			
		||||
        SDL_free(set);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										88
									
								
								src/ext/net/SDLnetsys.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/ext/net/SDLnetsys.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
/*
 | 
			
		||||
  SDL_net:  An example cross-platform network library for use with SDL
 | 
			
		||||
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
 | 
			
		||||
 | 
			
		||||
  This software is provided 'as-is', without any express or implied
 | 
			
		||||
  warranty.  In no event will the authors be held liable for any damages
 | 
			
		||||
  arising from the use of this software.
 | 
			
		||||
 | 
			
		||||
  Permission is granted to anyone to use this software for any purpose,
 | 
			
		||||
  including commercial applications, and to alter it and redistribute it
 | 
			
		||||
  freely, subject to the following restrictions:
 | 
			
		||||
 | 
			
		||||
  1. The origin of this software must not be misrepresented; you must not
 | 
			
		||||
     claim that you wrote the original software. If you use this software
 | 
			
		||||
     in a product, an acknowledgment in the product documentation would be
 | 
			
		||||
     appreciated but is not required.
 | 
			
		||||
  2. Altered source versions must be plainly marked as such, and must not be
 | 
			
		||||
     misrepresented as being the original software.
 | 
			
		||||
  3. This notice may not be removed or altered from any source distribution.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* $Id$ */
 | 
			
		||||
 | 
			
		||||
/* Include normal system headers */
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32_WCE
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
 | 
			
		||||
/* Include system network headers */
 | 
			
		||||
#if defined(__WIN32__) || defined(WIN32) || defined(__WINRT__)
 | 
			
		||||
#define __USE_W32_SOCKETS
 | 
			
		||||
#include <winsock2.h>
 | 
			
		||||
#include <ws2tcpip.h>
 | 
			
		||||
#include <iphlpapi.h>
 | 
			
		||||
#else /* UNIX */
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#ifdef __FreeBSD__
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#ifndef __BEOS__
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <netinet/tcp.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <net/if.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#endif /* WIN32 */
 | 
			
		||||
 | 
			
		||||
/* FIXME: What platforms need this? */
 | 
			
		||||
#if 0
 | 
			
		||||
typedef Uint32 socklen_t;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* System-dependent definitions */
 | 
			
		||||
#ifndef __USE_W32_SOCKETS
 | 
			
		||||
#ifdef __OS2__
 | 
			
		||||
#define closesocket     soclose
 | 
			
		||||
#else  /* !__OS2__ */
 | 
			
		||||
#define closesocket close
 | 
			
		||||
#endif /* __OS2__ */
 | 
			
		||||
#define SOCKET  int
 | 
			
		||||
#define INVALID_SOCKET  -1
 | 
			
		||||
#define SOCKET_ERROR    -1
 | 
			
		||||
#endif /* __USE_W32_SOCKETS */
 | 
			
		||||
 | 
			
		||||
#ifdef __USE_W32_SOCKETS
 | 
			
		||||
#define SDLNet_GetLastError WSAGetLastError
 | 
			
		||||
#define SDLNet_SetLastError WSASetLastError
 | 
			
		||||
#ifndef EINTR
 | 
			
		||||
#define EINTR WSAEINTR
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
int SDLNet_GetLastError(void);
 | 
			
		||||
void SDLNet_SetLastError(int err);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user