789 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			789 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #define HL_NAME(n) fmt_##n
 | |
| //#include <png.h>
 | |
| #include <hl.h>
 | |
| 
 | |
| #if defined(HL_CONSOLE) && !defined(HL_XBO)
 | |
| //extern bool sys_jpg_decode( vbyte *data, int dataLen, vbyte *out, int width, int height, int stride, int format, int flags );
 | |
| #else
 | |
| //#	include <turbojpeg.h>
 | |
| #endif
 | |
| 
 | |
| #include <zlib.h>
 | |
| //#include <vorbis/vorbisfile.h>
 | |
| 
 | |
| //#define MINIMP3_IMPLEMENTATION
 | |
| //#define MINIMP3_FLOAT_OUTPUT
 | |
| //#include <minimp3.h>
 | |
| 
 | |
| /* ------------------------------------------------- IMG --------------------------------------------------- */
 | |
| 
 | |
| typedef struct {
 | |
| 	unsigned char a,r,g,b;
 | |
| } pixel;
 | |
| 
 | |
| /*HL_PRIM bool HL_NAME(jpg_decode)( vbyte *data, int dataLen, vbyte *out, int width, int height, int stride, int format, int flags ) {
 | |
| #if defined(HL_CONSOLE) && !defined(HL_XBO)
 | |
| 	hl_blocking(true);
 | |
| 	bool b = sys_jpg_decode(data, dataLen, out, width, height, stride, format, flags);
 | |
| 	hl_blocking(false);
 | |
| 	return b;
 | |
| #else
 | |
| 	hl_blocking(true);
 | |
| 	tjhandle h = tjInitDecompress();
 | |
| 	int result;
 | |
| 	result = tjDecompress2(h,data,dataLen,out,width,stride,height,format,(flags & 1 ? TJFLAG_BOTTOMUP : 0));
 | |
| 	tjDestroy(h);
 | |
| 	hl_blocking(false);
 | |
| 	return result == 0;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| HL_PRIM bool HL_NAME(png_decode)( vbyte *data, int dataLen, vbyte *out, int width, int height, int stride, int format, int flags ) {
 | |
| #	ifdef PNG_IMAGE_VERSION
 | |
| 	png_image img;
 | |
| 	hl_blocking(true);
 | |
| 	memset(&img, 0, sizeof(img));
 | |
| 	img.version = PNG_IMAGE_VERSION;
 | |
| 	if( png_image_begin_read_from_memory(&img,data,dataLen) == 0 ) {
 | |
| 		hl_blocking(false);
 | |
| 		png_image_free(&img);
 | |
| 		return false;
 | |
| 	}
 | |
| 	switch( format ) {
 | |
| 	case 0:
 | |
| 		img.format = PNG_FORMAT_RGB;
 | |
| 		break;
 | |
| 	case 1:
 | |
| 		img.format = PNG_FORMAT_BGR;
 | |
| 		break;
 | |
| 	case 7:
 | |
| 		img.format = PNG_FORMAT_RGBA;
 | |
| 		break;
 | |
| 	case 8:
 | |
| 		img.format = PNG_FORMAT_BGRA;
 | |
| 		break;
 | |
| 	case 9:
 | |
| 		img.format = PNG_FORMAT_ABGR;
 | |
| 		break;
 | |
| 	case 10:
 | |
| 		img.format = PNG_FORMAT_ARGB;
 | |
| 		break;
 | |
| 	case 12:
 | |
| 		img.format = PNG_FORMAT_LINEAR_Y;
 | |
| 		break;
 | |
| 	case 13:
 | |
| 		img.format = PNG_FORMAT_LINEAR_RGB;
 | |
| 		break;
 | |
| 	case 14:
 | |
| 		img.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
 | |
| 		break;
 | |
| 	case 15:
 | |
| 		img.format = PNG_FORMAT_LINEAR_Y_ALPHA;
 | |
| 		break;
 | |
| 	default:
 | |
| 		hl_blocking(false);
 | |
| 		png_image_free(&img);
 | |
| 		hl_error("Unsupported format");
 | |
| 		break;
 | |
| 	}
 | |
| 	if( img.width != width || img.height != height ) {
 | |
| 		hl_blocking(false);
 | |
| 		png_image_free(&img);
 | |
| 		return false;
 | |
| 	}
 | |
| 	if( png_image_finish_read(&img,NULL,out,stride * (flags & 1 ? -1 : 1),NULL) == 0 ) {
 | |
| 		hl_blocking(false);
 | |
| 		png_image_free(&img);
 | |
| 		return false;
 | |
| 	}
 | |
| 	hl_blocking(false);
 | |
| 	png_image_free(&img);
 | |
| #	else
 | |
| 	hl_error("PNG support is missing for this libPNG version");
 | |
| #	endif
 | |
| 	return true;
 | |
| }*/
 | |
| 
 | |
| HL_PRIM void HL_NAME(img_scale)( vbyte *out, int outPos, int outStride, int outWidth, int outHeight, vbyte *in, int inPos, int inStride, int inWidth, int inHeight, int flags ) {
 | |
| 	int x, y;
 | |
| 	float scaleX = outWidth <= 1 ? 0.0f : (float)((inWidth - 1.001f) / (outWidth - 1));
 | |
| 	float scaleY = outHeight <= 1 ? 0.0f : (float)((inHeight - 1.001f) / (outHeight - 1));
 | |
| 	out += outPos;
 | |
| 	in += inPos;
 | |
| 	hl_blocking(true);
 | |
| 	for(y=0;y<outHeight;y++) {
 | |
| 		for(x=0;x<outWidth;x++) {
 | |
| 			float fx = x * scaleX;
 | |
| 			float fy = y * scaleY;
 | |
| 			int ix = (int)fx;
 | |
| 			int iy = (int)fy;
 | |
| 			if( (flags & 1) == 0 ) {
 | |
| 				// nearest
 | |
| 				vbyte *rin = in + iy * inStride;
 | |
| 				*(pixel*)out = *(pixel*)(rin + (ix<<2));
 | |
| 				out += 4;
 | |
| 			} else {
 | |
| 				// bilinear
 | |
| 				float rx = fx - ix;
 | |
| 				float ry = fy - iy;
 | |
| 				float rx1 = 1.0f - rx;
 | |
| 				float ry1 = 1.0f - ry;
 | |
| 				int w1 = (int)(rx1 * ry1 * 256.0f);
 | |
| 				int w2 = (int)(rx * ry1 * 256.0f);
 | |
| 				int w3 = (int)(rx1 * ry * 256.0f);
 | |
| 				int w4 = (int)(rx * ry * 256.0f);
 | |
| 				vbyte *rin = in + iy * inStride;
 | |
| 				pixel p1 = *(pixel*)(rin + (ix<<2));
 | |
| 				pixel p2 = *(pixel*)(rin + ((ix + 1)<<2));
 | |
| 				pixel p3 = *(pixel*)(rin + inStride + (ix<<2));
 | |
| 				pixel p4 = *(pixel*)(rin + inStride + ((ix + 1)<<2));
 | |
| 				*out++ = (unsigned char)((p1.a * w1 + p2.a * w2 + p3.a * w3 + p4.a * w4 + 128)>>8);
 | |
| 				*out++ = (unsigned char)((p1.r * w1 + p2.r * w2 + p3.r * w3 + p4.r * w4 + 128)>>8);
 | |
| 				*out++ = (unsigned char)((p1.g * w1 + p2.g * w2 + p3.g * w3 + p4.g * w4 + 128)>>8);
 | |
| 				*out++ = (unsigned char)((p1.b * w1 + p2.b * w2 + p3.b * w3 + p4.b * w4 + 128)>>8);
 | |
| 			}
 | |
| 		}
 | |
| 		out += outStride - (outWidth << 2);
 | |
| 	}
 | |
| 	hl_blocking(false);
 | |
| }
 | |
| 
 | |
| 
 | |
| DEFINE_PRIM(_BOOL, jpg_decode, _BYTES _I32 _BYTES _I32 _I32 _I32 _I32 _I32);
 | |
| DEFINE_PRIM(_BOOL, png_decode, _BYTES _I32 _BYTES _I32 _I32 _I32 _I32 _I32);
 | |
| DEFINE_PRIM(_VOID, img_scale, _BYTES _I32 _I32 _I32 _I32 _BYTES _I32 _I32 _I32 _I32 _I32);
 | |
| 
 | |
| 
 | |
| /* ------------------------------------------------- ZLIB --------------------------------------------------- */
 | |
| 
 | |
| typedef struct _fmt_zip fmt_zip;
 | |
| struct _fmt_zip {
 | |
| 	void (*finalize)( fmt_zip * );
 | |
| 	z_stream *z;
 | |
| 	int flush;
 | |
| 	bool inflate;
 | |
| };
 | |
| 
 | |
| static void free_stream_inf( fmt_zip *v ) {
 | |
| 	if( v->inflate )
 | |
| 		inflateEnd(v->z); // no error
 | |
| 	else
 | |
| 		deflateEnd(v->z);
 | |
| 	free(v->z);
 | |
| 	v->z = NULL;
 | |
| 	v->finalize = NULL;
 | |
| }
 | |
| 
 | |
| static void zlib_error( z_stream *z, int err ) {
 | |
| 	hl_buffer *b = hl_alloc_buffer();
 | |
| 	vdynamic *d;
 | |
| 	hl_buffer_cstr(b, "ZLib Error : ");
 | |
| 	if( z && z->msg ) {
 | |
| 		hl_buffer_cstr(b,z->msg);
 | |
| 		hl_buffer_cstr(b," (");
 | |
| 	}
 | |
| 	d = hl_alloc_dynamic(&hlt_i32);
 | |
| 	d->v.i = err;
 | |
| 	hl_buffer_val(b,d);
 | |
| 	if( z && z->msg )
 | |
| 		hl_buffer_char(b,')');
 | |
| 	d = hl_alloc_dynamic(&hlt_bytes);
 | |
| 	d->v.ptr = hl_buffer_content(b,NULL);
 | |
| 	hl_throw(d);
 | |
| }
 | |
| 
 | |
| HL_PRIM fmt_zip *HL_NAME(inflate_init)( int wbits ) {
 | |
| 	z_stream *z;
 | |
| 	int err;
 | |
| 	fmt_zip *s;
 | |
| 	if( wbits == 0 )
 | |
| 		wbits = MAX_WBITS;
 | |
| 	z = (z_stream*)malloc(sizeof(z_stream));
 | |
| 	memset(z,0,sizeof(z_stream));
 | |
| 	if( (err = inflateInit2(z,wbits)) != Z_OK ) {
 | |
| 		free(z);
 | |
| 		zlib_error(NULL,err);
 | |
| 	}
 | |
| 	s = (fmt_zip*)hl_gc_alloc_finalizer(sizeof(fmt_zip));
 | |
| 	s->finalize = free_stream_inf;
 | |
| 	s->flush = Z_NO_FLUSH;
 | |
| 	s->z = z;
 | |
| 	s->inflate = true;
 | |
| 	return s;
 | |
| }
 | |
| 
 | |
| HL_PRIM fmt_zip *HL_NAME(deflate_init)( int level ) {
 | |
| 	z_stream *z;
 | |
| 	int err;
 | |
| 	fmt_zip *s;
 | |
| 	z = (z_stream*)malloc(sizeof(z_stream));
 | |
| 	memset(z,0,sizeof(z_stream));
 | |
| 	if( (err = deflateInit(z,level)) != Z_OK ) {
 | |
| 		free(z);
 | |
| 		zlib_error(NULL,err);
 | |
| 	}
 | |
| 	s = (fmt_zip*)hl_gc_alloc_finalizer(sizeof(fmt_zip));
 | |
| 	s->finalize = free_stream_inf;
 | |
| 	s->flush = Z_NO_FLUSH;
 | |
| 	s->z = z;
 | |
| 	s->inflate = false;
 | |
| 	return s;
 | |
| }
 | |
| 
 | |
| HL_PRIM void HL_NAME(zip_end)( fmt_zip *z ) {
 | |
| 	free_stream_inf(z);
 | |
| }
 | |
| 
 | |
| HL_PRIM void HL_NAME(zip_flush_mode)( fmt_zip *z, int flush ) {
 | |
| 	switch( flush ) {
 | |
| 	case 0:
 | |
| 		z->flush = Z_NO_FLUSH;
 | |
| 		break;
 | |
| 	case 1:
 | |
| 		z->flush = Z_SYNC_FLUSH;
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		z->flush = Z_FULL_FLUSH;
 | |
| 		break;
 | |
| 	case 3:
 | |
| 		z->flush = Z_FINISH;
 | |
| 		break;
 | |
| 	case 4:
 | |
| 		z->flush = Z_BLOCK;
 | |
| 		break;
 | |
| 	default:
 | |
| 		hl_error("Invalid flush mode %d",flush);
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| HL_PRIM bool HL_NAME(inflate_buffer)( fmt_zip *zip, vbyte *src, int srcpos, int srclen, vbyte *dst, int dstpos, int dstlen, int *read, int *write ) {
 | |
| 	int slen, dlen, err;
 | |
| 	z_stream *z = zip->z;
 | |
| 	slen = srclen - srcpos;
 | |
| 	dlen = dstlen - dstpos;
 | |
| 	if( srcpos < 0 || dstpos < 0 || slen < 0 || dlen < 0 )
 | |
| 		hl_error("Out of range");
 | |
| 	hl_blocking(true);
 | |
| 	z->next_in = (Bytef*)(src + srcpos);
 | |
| 	z->next_out = (Bytef*)(dst + dstpos);
 | |
| 	z->avail_in = slen;
 | |
| 	z->avail_out = dlen;
 | |
| 	if( (err = inflate(z,zip->flush)) < 0 ) {
 | |
| 		hl_blocking(false);
 | |
| 		zlib_error(z,err);
 | |
| 	}
 | |
| 	z->next_in = NULL;
 | |
| 	z->next_out = NULL;
 | |
| 	*read = slen - z->avail_in;
 | |
| 	*write = dlen - z->avail_out;
 | |
| 	hl_blocking(false);
 | |
| 	return err == Z_STREAM_END;
 | |
| }
 | |
| 
 | |
| HL_PRIM bool HL_NAME(deflate_buffer)( fmt_zip *zip, vbyte *src, int srcpos, int srclen, vbyte *dst, int dstpos, int dstlen, int *read, int *write ) {
 | |
| 	int slen, dlen, err;
 | |
| 	z_stream *z = zip->z;
 | |
| 	slen = srclen - srcpos;
 | |
| 	dlen = dstlen - dstpos;
 | |
| 	if( srcpos < 0 || dstpos < 0 || slen < 0 || dlen < 0 )
 | |
| 		hl_error("Out of range");
 | |
| 	hl_blocking(true);
 | |
| 	z->next_in = (Bytef*)(src + srcpos);
 | |
| 	z->next_out = (Bytef*)(dst + dstpos);
 | |
| 	z->avail_in = slen;
 | |
| 	z->avail_out = dlen;
 | |
| 	if( (err = deflate(z,zip->flush)) < 0 ) {
 | |
| 		hl_blocking(false);
 | |
| 		zlib_error(z,err);
 | |
| 	}
 | |
| 	z->next_in = NULL;
 | |
| 	z->next_out = NULL;
 | |
| 	*read = slen - z->avail_in;
 | |
| 	*write = dlen - z->avail_out;
 | |
| 	hl_blocking(false);
 | |
| 	return err == Z_STREAM_END;
 | |
| }
 | |
| 
 | |
| HL_PRIM int HL_NAME(deflate_bound)( fmt_zip *zip, int size ) {
 | |
| 	return deflateBound(zip->z,size);
 | |
| }
 | |
| 
 | |
| #define _ZIP _ABSTRACT(fmt_zip)
 | |
| 
 | |
| DEFINE_PRIM(_ZIP, inflate_init, _I32);
 | |
| DEFINE_PRIM(_ZIP, deflate_init, _I32);
 | |
| DEFINE_PRIM(_I32, deflate_bound, _ZIP _I32);
 | |
| DEFINE_PRIM(_VOID, zip_end, _ZIP);
 | |
| DEFINE_PRIM(_VOID, zip_flush_mode, _ZIP _I32);
 | |
| DEFINE_PRIM(_BOOL, inflate_buffer, _ZIP _BYTES _I32 _I32 _BYTES _I32 _I32 _REF(_I32) _REF(_I32));
 | |
| DEFINE_PRIM(_BOOL, deflate_buffer, _ZIP _BYTES _I32 _I32 _BYTES _I32 _I32 _REF(_I32) _REF(_I32));
 | |
| 
 | |
| /* ----------------------------------------------- SOUND : OGG ------------------------------------------------ */
 | |
| 
 | |
| /*typedef struct _fmt_ogg fmt_ogg;
 | |
| struct _fmt_ogg {
 | |
| 	void (*finalize)( fmt_ogg * );
 | |
| 	OggVorbis_File f;
 | |
| 	char *bytes;
 | |
| 	int pos;
 | |
| 	int size;
 | |
| 	int section;
 | |
| };
 | |
| 
 | |
| static void ogg_finalize( fmt_ogg *o ) {
 | |
| 	ov_clear(&o->f);
 | |
| }
 | |
| 
 | |
| static size_t ogg_memread( void *ptr, int size, int count, fmt_ogg *o ) {
 | |
| 	int len = size * count;
 | |
| 	if( o->pos + len > o->size )
 | |
| 		len = o->size - o->pos;
 | |
| 	memcpy(ptr, o->bytes + o->pos, len);
 | |
| 	o->pos += len;
 | |
| 	return len;
 | |
| }
 | |
| 
 | |
| static int ogg_memseek( fmt_ogg *o, ogg_int64_t _offset, int mode ) {
 | |
| 	int offset = (int)_offset;
 | |
| 	switch( mode ) {
 | |
| 	case SEEK_SET:
 | |
| 		if( offset < 0 || offset > o->size ) return 1;
 | |
| 		o->pos = offset;
 | |
| 		break;
 | |
| 	case SEEK_CUR:
 | |
| 		if( o->pos + offset < 0 || o->pos + offset > o->size ) return 1;
 | |
| 		o->pos += offset;
 | |
| 		break;
 | |
| 	case SEEK_END:
 | |
| 		if( offset < 0 || offset > o->size ) return 1;
 | |
| 		o->pos = o->size - offset;
 | |
| 		break;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static long ogg_memtell( fmt_ogg *o ) {
 | |
| 	return o->pos;
 | |
| }
 | |
| 
 | |
| static ov_callbacks OV_CALLBACKS_MEMORY = {
 | |
|   (size_t (*)(void *, size_t, size_t, void *))  ogg_memread,
 | |
|   (int (*)(void *, ogg_int64_t, int))           ogg_memseek,
 | |
|   (int (*)(void *))                             NULL,
 | |
|   (long (*)(void *))                            ogg_memtell
 | |
| };
 | |
| 
 | |
| HL_PRIM fmt_ogg *HL_NAME(ogg_open)( char *bytes, int size ) {
 | |
| 	fmt_ogg *o = (fmt_ogg*)hl_gc_alloc_finalizer(sizeof(fmt_ogg));
 | |
| 	o->finalize = NULL;
 | |
| 	o->bytes = bytes;
 | |
| 	o->size = size;
 | |
| 	o->pos = 0;
 | |
| 	if( ov_open_callbacks(o,&o->f,NULL,0,OV_CALLBACKS_MEMORY) != 0 )
 | |
| 		return NULL;
 | |
| 	o->finalize = ogg_finalize;
 | |
| 	return o;
 | |
| }
 | |
| 
 | |
| HL_PRIM void HL_NAME(ogg_info)( fmt_ogg *o, int *bitrate, int *freq, int *samples, int *channels ) {
 | |
| 	vorbis_info *i = ov_info(&o->f,-1);
 | |
| 	*bitrate = i->bitrate_nominal;
 | |
| 	*freq = i->rate;
 | |
| 	*channels = i->channels;
 | |
| 	*samples = (int)ov_pcm_total(&o->f, -1);
 | |
| }
 | |
| 
 | |
| HL_PRIM int HL_NAME(ogg_tell)( fmt_ogg *o ) {
 | |
| 	return (int)ov_pcm_tell(&o->f); // overflow at 12 hours @48 Khz
 | |
| }
 | |
| 
 | |
| HL_PRIM bool HL_NAME(ogg_seek)( fmt_ogg *o, int sample ) {
 | |
| 	return ov_pcm_seek(&o->f,sample) == 0;
 | |
| }
 | |
| 
 | |
| #define OGGFMT_I8			1
 | |
| #define OGGFMT_I16			2
 | |
| //#define OGGFMT_F32		3
 | |
| #define OGGFMT_BIGENDIAN	128
 | |
| #define OGGFMT_UNSIGNED		256
 | |
| 
 | |
| HL_PRIM int HL_NAME(ogg_read)( fmt_ogg *o, char *output, int size, int format ) {
 | |
| 	int ret = -1;
 | |
| 	hl_blocking(true);
 | |
| 	switch( format&127 ) {
 | |
| 	case OGGFMT_I8:
 | |
| 	case OGGFMT_I16:
 | |
| 		ret = ov_read(&o->f, output, size, (format & OGGFMT_BIGENDIAN) != 0, format&3, (format & OGGFMT_UNSIGNED) == 0, &o->section);
 | |
| 		break;
 | |
| //	case OGGFMT_F32:
 | |
| //		-- this decodes separates channels instead of mixed single buffer one
 | |
| //		return ov_read_float(&o->f, output, size, (format & OGGFMT_BIGENDIAN) != 0, format&3, (format & OGGFMT_UNSIGNED) == 0, &o->section);
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 	hl_blocking(false);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| #define _OGG _ABSTRACT(fmt_ogg)
 | |
| 
 | |
| DEFINE_PRIM(_OGG, ogg_open, _BYTES _I32);
 | |
| DEFINE_PRIM(_VOID, ogg_info, _OGG _REF(_I32) _REF(_I32) _REF(_I32) _REF(_I32));
 | |
| DEFINE_PRIM(_I32, ogg_tell, _OGG);
 | |
| DEFINE_PRIM(_BOOL, ogg_seek, _OGG _I32);
 | |
| DEFINE_PRIM(_I32, ogg_read, _OGG _BYTES _I32 _I32);*/
 | |
| 
 | |
| /* ----------------------------------------------- SOUND : MP3 ------------------------------------------------ */
 | |
| 
 | |
| /*typedef struct _fmt_mp3 fmt_mp3;
 | |
| struct _fmt_mp3 {
 | |
| 	mp3dec_t dec;
 | |
| 	mp3dec_frame_info_t info;
 | |
| 	mp3d_sample_t pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
 | |
| };*/
 | |
| 
 | |
| // Allocate MP3 reader.
 | |
| /*HL_PRIM fmt_mp3 *HL_NAME(mp3_open)() {
 | |
| 	fmt_mp3 *o = (fmt_mp3*)hl_gc_alloc_noptr(sizeof(fmt_mp3));
 | |
| 	mp3dec_init(&o->dec);
 | |
| 	return o;
 | |
| }*/
 | |
| 
 | |
| /**
 | |
| 	Retreive last decoded frame information.
 | |
| 	@param bitrate_kbps Bitrate of the frame
 | |
| 	@param channels Total amount of channels in the frame.
 | |
| 	@param frame_bytes The size of the frame in the input stream,
 | |
| 	@param hz
 | |
| 	@param layer Mpeg Layer index (usually 3).
 | |
| **/
 | |
| /*HL_PRIM void HL_NAME(mp3_frame_info)(fmt_mp3 *o, int *bitrate_kbps, int *channels, int *frame_bytes, int *hz, int *layer) {
 | |
| 	*bitrate_kbps = o->info.bitrate_kbps;
 | |
| 	*channels = o->info.channels;
 | |
| 	*frame_bytes = o->info.frame_bytes;
 | |
| 	*hz = o->info.hz;
 | |
| 	*layer = o->info.layer;
 | |
| }*/
 | |
| 
 | |
| /**
 | |
| 	Decodes a single frame from input stream and writes result to output.
 | |
| 	Decoded samples are in Float32 format. Output bytes should contain enough space to fit entire frame in.
 | |
| 	To calculate required output size, follow next formula: `samples * channels * 4`.
 | |
| 	For Layer 1, amount of frames is 384, MPEG 2 Layer 2 is 576 and 1152 otherwise. Using 1152 samples is the safest.
 | |
| 	@param o Allocated MP3 reader.
 | |
| 	@param bytes Input stream.
 | |
| 	@param size Input stream size.
 | |
| 	@param position Input stream offset.
 | |
| 	@param output Output stream.
 | |
| 	@param outputSize Output stream size.
 | |
| 	@param offset Output stream write offset.
 | |
| 	@returns 0 if no MP3 data was found (end of stream/invalid data), -1 if either input buffer position invalid or output size is insufficent.
 | |
| 		Amount of decoded samples otherwise.
 | |
| **/
 | |
| /*HL_PRIM int HL_NAME(mp3_decode_frame)( fmt_mp3 *o, char *bytes, int size, int position, char *output, int outputSize, int offset ) {
 | |
| 
 | |
| 	// Out of mp3 file bounds.
 | |
| 	if ( position < 0 || size <= position )
 | |
| 		return -1;
 | |
| 
 | |
| 	int samples = 0;
 | |
| 	hl_blocking(true);
 | |
| 
 | |
| 	do {
 | |
| 		samples = mp3dec_decode_frame(&o->dec, (unsigned char*)bytes + position, size - position, o->pcm, &o->info);
 | |
| 		// Try to read until found mp3 data or EOF.
 | |
| 		if ( samples != 0 || o->info.frame_bytes == 0 )
 | |
| 			break;
 | |
| 		position += o->info.frame_bytes;
 | |
| 	} while ( size > position );
 | |
| 
 | |
| 	// No or invalid MP3 data.
 | |
| 	if ( samples == 0 || o->info.frame_bytes == 0 ) {
 | |
| 		hl_blocking(false);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	int decodedSize = samples * o->info.channels * sizeof(mp3d_sample_t);
 | |
| 	// Insufficent output buffer size.
 | |
| 	if ( outputSize - offset < decodedSize ) {
 | |
| 		hl_blocking(false);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	memcpy( (void *)(output + offset), (void *)o->pcm, decodedSize );
 | |
| 
 | |
| 	hl_blocking(false);
 | |
| 	return samples;
 | |
| }
 | |
| 
 | |
| #define _MP3 _ABSTRACT(fmt_mp3)
 | |
| 
 | |
| DEFINE_PRIM(_MP3, mp3_open, _BYTES _I32);
 | |
| DEFINE_PRIM(_VOID, mp3_frame_info, _MP3 _REF(_I32) _REF(_I32) _REF(_I32) _REF(_I32) _REF(_I32))
 | |
| DEFINE_PRIM(_I32, mp3_decode_frame, _MP3 _BYTES _I32 _I32 _BYTES _I32 _I32);*/
 | |
| 
 | |
| /* ------------------------------------------------- CRYPTO --------------------------------------------------- */
 | |
| 
 | |
| typedef unsigned int uint32;
 | |
| typedef unsigned char uint8;
 | |
| 
 | |
| typedef struct {
 | |
|     uint32 total[2];
 | |
|     uint32 state[4];
 | |
|     uint8 buffer[64];
 | |
| } md5_context;
 | |
| 
 | |
| #define GET_UINT32(n,b,i)                       \
 | |
| {                                               \
 | |
|     (n) = ( (uint32) (b)[(i)    ]       )       \
 | |
|         | ( (uint32) (b)[(i) + 1] <<  8 )       \
 | |
|         | ( (uint32) (b)[(i) + 2] << 16 )       \
 | |
|         | ( (uint32) (b)[(i) + 3] << 24 );      \
 | |
| }
 | |
| 
 | |
| #define PUT_UINT32(n,b,i)                       \
 | |
| {                                               \
 | |
|     (b)[(i)    ] = (uint8) ( (n)       );       \
 | |
|     (b)[(i) + 1] = (uint8) ( (n) >>  8 );       \
 | |
|     (b)[(i) + 2] = (uint8) ( (n) >> 16 );       \
 | |
|     (b)[(i) + 3] = (uint8) ( (n) >> 24 );       \
 | |
| }
 | |
| 
 | |
| static void md5_starts( md5_context *ctx ) {
 | |
|     ctx->total[0] = 0;
 | |
|     ctx->total[1] = 0;
 | |
|     ctx->state[0] = 0x67452301;
 | |
|     ctx->state[1] = 0xEFCDAB89;
 | |
|     ctx->state[2] = 0x98BADCFE;
 | |
|     ctx->state[3] = 0x10325476;
 | |
| }
 | |
| 
 | |
| static void md5_process( md5_context *ctx, uint8 data[64] ) {
 | |
|     uint32 X[16], A, B, C, D;
 | |
|     GET_UINT32( X[0],  data,  0 );
 | |
|     GET_UINT32( X[1],  data,  4 );
 | |
|     GET_UINT32( X[2],  data,  8 );
 | |
|     GET_UINT32( X[3],  data, 12 );
 | |
|     GET_UINT32( X[4],  data, 16 );
 | |
|     GET_UINT32( X[5],  data, 20 );
 | |
|     GET_UINT32( X[6],  data, 24 );
 | |
|     GET_UINT32( X[7],  data, 28 );
 | |
|     GET_UINT32( X[8],  data, 32 );
 | |
|     GET_UINT32( X[9],  data, 36 );
 | |
|     GET_UINT32( X[10], data, 40 );
 | |
|     GET_UINT32( X[11], data, 44 );
 | |
|     GET_UINT32( X[12], data, 48 );
 | |
|     GET_UINT32( X[13], data, 52 );
 | |
|     GET_UINT32( X[14], data, 56 );
 | |
|     GET_UINT32( X[15], data, 60 );
 | |
| 
 | |
| #define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
 | |
| 
 | |
| #define P(a,b,c,d,k,s,t)                                \
 | |
| {                                                       \
 | |
|     a += F(b,c,d) + X[k] + t; a = S(a,s) + b;           \
 | |
| }
 | |
| 
 | |
|     A = ctx->state[0];
 | |
|     B = ctx->state[1];
 | |
|     C = ctx->state[2];
 | |
|     D = ctx->state[3];
 | |
| 
 | |
| #define F(x,y,z) (z ^ (x & (y ^ z)))
 | |
| 
 | |
|     P( A, B, C, D,  0,  7, 0xD76AA478 );
 | |
|     P( D, A, B, C,  1, 12, 0xE8C7B756 );
 | |
|     P( C, D, A, B,  2, 17, 0x242070DB );
 | |
|     P( B, C, D, A,  3, 22, 0xC1BDCEEE );
 | |
|     P( A, B, C, D,  4,  7, 0xF57C0FAF );
 | |
|     P( D, A, B, C,  5, 12, 0x4787C62A );
 | |
|     P( C, D, A, B,  6, 17, 0xA8304613 );
 | |
|     P( B, C, D, A,  7, 22, 0xFD469501 );
 | |
|     P( A, B, C, D,  8,  7, 0x698098D8 );
 | |
|     P( D, A, B, C,  9, 12, 0x8B44F7AF );
 | |
|     P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
 | |
|     P( B, C, D, A, 11, 22, 0x895CD7BE );
 | |
|     P( A, B, C, D, 12,  7, 0x6B901122 );
 | |
|     P( D, A, B, C, 13, 12, 0xFD987193 );
 | |
|     P( C, D, A, B, 14, 17, 0xA679438E );
 | |
|     P( B, C, D, A, 15, 22, 0x49B40821 );
 | |
| 
 | |
| #undef F
 | |
| 
 | |
| #define F(x,y,z) (y ^ (z & (x ^ y)))
 | |
| 
 | |
|     P( A, B, C, D,  1,  5, 0xF61E2562 );
 | |
|     P( D, A, B, C,  6,  9, 0xC040B340 );
 | |
|     P( C, D, A, B, 11, 14, 0x265E5A51 );
 | |
|     P( B, C, D, A,  0, 20, 0xE9B6C7AA );
 | |
|     P( A, B, C, D,  5,  5, 0xD62F105D );
 | |
|     P( D, A, B, C, 10,  9, 0x02441453 );
 | |
|     P( C, D, A, B, 15, 14, 0xD8A1E681 );
 | |
|     P( B, C, D, A,  4, 20, 0xE7D3FBC8 );
 | |
|     P( A, B, C, D,  9,  5, 0x21E1CDE6 );
 | |
|     P( D, A, B, C, 14,  9, 0xC33707D6 );
 | |
|     P( C, D, A, B,  3, 14, 0xF4D50D87 );
 | |
|     P( B, C, D, A,  8, 20, 0x455A14ED );
 | |
|     P( A, B, C, D, 13,  5, 0xA9E3E905 );
 | |
|     P( D, A, B, C,  2,  9, 0xFCEFA3F8 );
 | |
|     P( C, D, A, B,  7, 14, 0x676F02D9 );
 | |
|     P( B, C, D, A, 12, 20, 0x8D2A4C8A );
 | |
| 
 | |
| #undef F
 | |
|     
 | |
| #define F(x,y,z) (x ^ y ^ z)
 | |
| 
 | |
|     P( A, B, C, D,  5,  4, 0xFFFA3942 );
 | |
|     P( D, A, B, C,  8, 11, 0x8771F681 );
 | |
|     P( C, D, A, B, 11, 16, 0x6D9D6122 );
 | |
|     P( B, C, D, A, 14, 23, 0xFDE5380C );
 | |
|     P( A, B, C, D,  1,  4, 0xA4BEEA44 );
 | |
|     P( D, A, B, C,  4, 11, 0x4BDECFA9 );
 | |
|     P( C, D, A, B,  7, 16, 0xF6BB4B60 );
 | |
|     P( B, C, D, A, 10, 23, 0xBEBFBC70 );
 | |
|     P( A, B, C, D, 13,  4, 0x289B7EC6 );
 | |
|     P( D, A, B, C,  0, 11, 0xEAA127FA );
 | |
|     P( C, D, A, B,  3, 16, 0xD4EF3085 );
 | |
|     P( B, C, D, A,  6, 23, 0x04881D05 );
 | |
|     P( A, B, C, D,  9,  4, 0xD9D4D039 );
 | |
|     P( D, A, B, C, 12, 11, 0xE6DB99E5 );
 | |
|     P( C, D, A, B, 15, 16, 0x1FA27CF8 );
 | |
|     P( B, C, D, A,  2, 23, 0xC4AC5665 );
 | |
| 
 | |
| #undef F
 | |
| 
 | |
| #define F(x,y,z) (y ^ (x | ~z))
 | |
| 
 | |
|     P( A, B, C, D,  0,  6, 0xF4292244 );
 | |
|     P( D, A, B, C,  7, 10, 0x432AFF97 );
 | |
|     P( C, D, A, B, 14, 15, 0xAB9423A7 );
 | |
|     P( B, C, D, A,  5, 21, 0xFC93A039 );
 | |
|     P( A, B, C, D, 12,  6, 0x655B59C3 );
 | |
|     P( D, A, B, C,  3, 10, 0x8F0CCC92 );
 | |
|     P( C, D, A, B, 10, 15, 0xFFEFF47D );
 | |
|     P( B, C, D, A,  1, 21, 0x85845DD1 );
 | |
|     P( A, B, C, D,  8,  6, 0x6FA87E4F );
 | |
|     P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
 | |
|     P( C, D, A, B,  6, 15, 0xA3014314 );
 | |
|     P( B, C, D, A, 13, 21, 0x4E0811A1 );
 | |
|     P( A, B, C, D,  4,  6, 0xF7537E82 );
 | |
|     P( D, A, B, C, 11, 10, 0xBD3AF235 );
 | |
|     P( C, D, A, B,  2, 15, 0x2AD7D2BB );
 | |
|     P( B, C, D, A,  9, 21, 0xEB86D391 );
 | |
| 
 | |
| #undef F
 | |
| 
 | |
|     ctx->state[0] += A;
 | |
|     ctx->state[1] += B;
 | |
|     ctx->state[2] += C;
 | |
|     ctx->state[3] += D;
 | |
| }
 | |
| 
 | |
| static void md5_update( md5_context *ctx, uint8 *input, uint32 length ) {
 | |
|     uint32 left, fill;
 | |
|     if( !length )
 | |
| 		return;
 | |
|     left = ctx->total[0] & 0x3F;
 | |
|     fill = 64 - left;
 | |
| 
 | |
|     ctx->total[0] += length;
 | |
|     ctx->total[0] &= 0xFFFFFFFF;
 | |
| 
 | |
|     if( ctx->total[0] < length )
 | |
|         ctx->total[1]++;
 | |
| 
 | |
|     if( left && length >= fill ) {
 | |
|         memcpy( (void *) (ctx->buffer + left),
 | |
|                 (void *) input, fill );
 | |
|         md5_process( ctx, ctx->buffer );
 | |
|         length -= fill;
 | |
|         input  += fill;
 | |
|         left = 0;
 | |
|     }
 | |
| 
 | |
|     while( length >= 64 ) {
 | |
|         md5_process( ctx, input );
 | |
|         length -= 64;
 | |
|         input  += 64;
 | |
|     }
 | |
| 
 | |
|     if( length ) {
 | |
|         memcpy( (void *) (ctx->buffer + left),
 | |
|                 (void *) input, length );
 | |
|     }
 | |
| }
 | |
| 
 | |
| static uint8 md5_padding[64] =
 | |
| {
 | |
|  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | |
|     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | |
|     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | |
|     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 | |
| };
 | |
| 
 | |
| static void md5_finish( md5_context *ctx, uint8 digest[16] ) {
 | |
|     uint32 last, padn;
 | |
|     uint32 high, low;
 | |
|     uint8 msglen[8];
 | |
| 
 | |
|     high = ( ctx->total[0] >> 29 )
 | |
|          | ( ctx->total[1] <<  3 );
 | |
|     low  = ( ctx->total[0] <<  3 );
 | |
| 
 | |
|     PUT_UINT32( low,  msglen, 0 );
 | |
|     PUT_UINT32( high, msglen, 4 );
 | |
| 
 | |
|     last = ctx->total[0] & 0x3F;
 | |
|     padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
 | |
| 
 | |
|     md5_update( ctx, md5_padding, padn );
 | |
|     md5_update( ctx, msglen, 8 );
 | |
| 
 | |
|     PUT_UINT32( ctx->state[0], digest,  0 );
 | |
|     PUT_UINT32( ctx->state[1], digest,  4 );
 | |
|     PUT_UINT32( ctx->state[2], digest,  8 );
 | |
|     PUT_UINT32( ctx->state[3], digest, 12 );
 | |
| }
 | |
| 
 | |
| #include "sha1.h"
 | |
| 
 | |
| HL_PRIM void HL_NAME(digest)( vbyte *out, vbyte *in, int length, int format ) {
 | |
| 	if( format & 256 ) {
 | |
| 		in = (vbyte*)hl_to_utf8((uchar*)in);
 | |
| 		length = (int)strlen((char*)in);
 | |
| 	}
 | |
| 	hl_blocking(true);
 | |
| 	switch( format & 0xFF ) {
 | |
| 	case 0:
 | |
| 		{
 | |
| 			md5_context ctx;
 | |
| 			md5_starts(&ctx);
 | |
| 			md5_update(&ctx,in,(uint32)length);
 | |
| 			md5_finish(&ctx,out);
 | |
| 		}
 | |
| 		break;
 | |
| 	case 1:
 | |
| 		{
 | |
| 			SHA1_CTX ctx;
 | |
| 			sha1_init(&ctx);
 | |
| 			sha1_update(&ctx,in,length);
 | |
| 			sha1_final(&ctx,out);
 | |
| 		}
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		*((int*)out) = crc32(*(int*)out, in, length);
 | |
| 		break;
 | |
| 	case 3:
 | |
| 		*((int*)out) = adler32(*(int*)out, in, length);
 | |
| 		break;
 | |
| 	default:
 | |
| 		hl_blocking(false);
 | |
| 		hl_error("Unknown digest format %d",format&0xFF);
 | |
| 		break;
 | |
| 	}
 | |
| 	hl_blocking(false);
 | |
| }
 | |
| 
 | |
| DEFINE_PRIM(_VOID, digest, _BYTES _BYTES _I32 _I32);
 |