forked from LeenkxTeam/LNXSDK
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);
|