Use libretro-common
Signed-off-by: Mahyar Koshkouei <mahyar.koshkouei@gmail.com>
This commit is contained in:
parent
a8f84fa9b4
commit
dde40b7732
2
Makefile
2
Makefile
|
@ -113,7 +113,7 @@ endif
|
||||||
|
|
||||||
OBJECTS := mpv-libretro.o
|
OBJECTS := mpv-libretro.o
|
||||||
LDFLAGS += -lmpv -lm
|
LDFLAGS += -lmpv -lm
|
||||||
CFLAGS += -Wall -pedantic -std=c99 -I.
|
CFLAGS += -Wall -pedantic -std=c99 -I./libretro-common/include/
|
||||||
|
|
||||||
ifneq (,$(findstring gles,$(platform)))
|
ifneq (,$(findstring gles,$(platform)))
|
||||||
LDFLAGS += -ldl
|
LDFLAGS += -ldl
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
*.o
|
|
@ -0,0 +1,318 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (audio_mix.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <audio/audio_mix.h>
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#elif defined(__ALTIVEC__)
|
||||||
|
#include <altivec.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <memalign.h>
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <audio/audio_mix.h>
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <audio/conversion/float_to_s16.h>
|
||||||
|
#include <audio/conversion/s16_to_float.h>
|
||||||
|
|
||||||
|
void audio_mix_volume_C(float *out, const float *in, float vol, size_t samples)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < samples; i++)
|
||||||
|
out[i] += in[i] * vol;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __SSE2__
|
||||||
|
void audio_mix_volume_SSE2(float *out, const float *in, float vol, size_t samples)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
__m128 volume = _mm_set1_ps(vol);
|
||||||
|
|
||||||
|
for (i = 0; i + 16 <= samples; i += 16, out += 16, in += 16)
|
||||||
|
{
|
||||||
|
unsigned j;
|
||||||
|
__m128 input[4];
|
||||||
|
__m128 additive[4];
|
||||||
|
|
||||||
|
input[0] = _mm_loadu_ps(out + 0);
|
||||||
|
input[1] = _mm_loadu_ps(out + 4);
|
||||||
|
input[2] = _mm_loadu_ps(out + 8);
|
||||||
|
input[3] = _mm_loadu_ps(out + 12);
|
||||||
|
|
||||||
|
additive[0] = _mm_mul_ps(volume, _mm_loadu_ps(in + 0));
|
||||||
|
additive[1] = _mm_mul_ps(volume, _mm_loadu_ps(in + 4));
|
||||||
|
additive[2] = _mm_mul_ps(volume, _mm_loadu_ps(in + 8));
|
||||||
|
additive[3] = _mm_mul_ps(volume, _mm_loadu_ps(in + 12));
|
||||||
|
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
_mm_storeu_ps(out + 4 * j, _mm_add_ps(input[j], additive[j]));
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_mix_volume_C(out, in, vol, samples - i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void audio_mix_free_chunk(audio_chunk_t *chunk)
|
||||||
|
{
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (chunk->rwav && chunk->rwav->samples)
|
||||||
|
{
|
||||||
|
/* rwav_free only frees the samples */
|
||||||
|
rwav_free(chunk->rwav);
|
||||||
|
free(chunk->rwav);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk->buf)
|
||||||
|
free(chunk->buf);
|
||||||
|
|
||||||
|
if (chunk->upsample_buf)
|
||||||
|
memalign_free(chunk->upsample_buf);
|
||||||
|
|
||||||
|
if (chunk->float_buf)
|
||||||
|
memalign_free(chunk->float_buf);
|
||||||
|
|
||||||
|
if (chunk->float_resample_buf)
|
||||||
|
memalign_free(chunk->float_resample_buf);
|
||||||
|
|
||||||
|
if (chunk->resample_buf)
|
||||||
|
memalign_free(chunk->resample_buf);
|
||||||
|
|
||||||
|
if (chunk->resampler && chunk->resampler_data)
|
||||||
|
chunk->resampler->free(chunk->resampler_data);
|
||||||
|
|
||||||
|
free(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate)
|
||||||
|
{
|
||||||
|
int sample_size;
|
||||||
|
ssize_t len = 0;
|
||||||
|
void *buf = NULL;
|
||||||
|
audio_chunk_t *chunk = (audio_chunk_t*)calloc(1, sizeof(*chunk));
|
||||||
|
|
||||||
|
if (!chunk)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!filestream_read_file(path, &buf, &len))
|
||||||
|
{
|
||||||
|
printf("Could not open WAV file for reading.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk->sample_rate = sample_rate;
|
||||||
|
chunk->buf = buf;
|
||||||
|
chunk->len = len;
|
||||||
|
chunk->rwav = (rwav_t*)malloc(sizeof(rwav_t));
|
||||||
|
|
||||||
|
if (rwav_load(chunk->rwav, chunk->buf, chunk->len) == RWAV_ITERATE_ERROR)
|
||||||
|
{
|
||||||
|
printf("error: could not load WAV file\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* numsamples does not know or care about
|
||||||
|
* multiple channels, but we need space for 2 */
|
||||||
|
chunk->upsample_buf = (int16_t*)memalign_alloc(128,
|
||||||
|
chunk->rwav->numsamples * 2 * sizeof(int16_t));
|
||||||
|
|
||||||
|
sample_size = chunk->rwav->bitspersample / 8;
|
||||||
|
|
||||||
|
if (sample_size == 1)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||||
|
{
|
||||||
|
uint8_t *sample = (
|
||||||
|
(uint8_t*)chunk->rwav->samples) +
|
||||||
|
(i * chunk->rwav->numchannels);
|
||||||
|
|
||||||
|
chunk->upsample_buf[i * 2] = (int16_t)((sample[0] - 128) << 8);
|
||||||
|
|
||||||
|
if (chunk->rwav->numchannels == 1)
|
||||||
|
chunk->upsample_buf[(i * 2) + 1] = (int16_t)((sample[0] - 128) << 8);
|
||||||
|
else if (chunk->rwav->numchannels == 2)
|
||||||
|
chunk->upsample_buf[(i * 2) + 1] = (int16_t)((sample[1] - 128) << 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sample_size == 2)
|
||||||
|
{
|
||||||
|
if (chunk->rwav->numchannels == 1)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||||
|
{
|
||||||
|
int16_t sample = ((int16_t*)chunk->rwav->samples)[i];
|
||||||
|
|
||||||
|
chunk->upsample_buf[i * 2] = sample;
|
||||||
|
chunk->upsample_buf[(i * 2) + 1] = sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (chunk->rwav->numchannels == 2)
|
||||||
|
memcpy(chunk->upsample_buf, chunk->rwav->samples, chunk->rwav->subchunk2size);
|
||||||
|
}
|
||||||
|
else if (sample_size != 2)
|
||||||
|
{
|
||||||
|
/* we don't support any other sample size besides 8 and 16-bit yet */
|
||||||
|
printf("error: we don't support a sample size of %d\n", sample_size);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sample_rate != (int)chunk->rwav->samplerate)
|
||||||
|
{
|
||||||
|
chunk->resample = true;
|
||||||
|
chunk->ratio = (double)sample_rate / chunk->rwav->samplerate;
|
||||||
|
|
||||||
|
retro_resampler_realloc(&chunk->resampler_data,
|
||||||
|
&chunk->resampler,
|
||||||
|
NULL,
|
||||||
|
RESAMPLER_QUALITY_DONTCARE,
|
||||||
|
chunk->ratio);
|
||||||
|
|
||||||
|
if (chunk->resampler && chunk->resampler_data)
|
||||||
|
{
|
||||||
|
struct resampler_data info;
|
||||||
|
|
||||||
|
chunk->float_buf = (float*)memalign_alloc(128, chunk->rwav->numsamples * 2 * chunk->ratio * sizeof(float));
|
||||||
|
|
||||||
|
/* why is *3 needed instead of just *2? does the sinc driver require more space than we know about? */
|
||||||
|
chunk->float_resample_buf = (float*)memalign_alloc(128, chunk->rwav->numsamples * 3 * chunk->ratio * sizeof(float));
|
||||||
|
|
||||||
|
convert_s16_to_float(chunk->float_buf, chunk->upsample_buf, chunk->rwav->numsamples * 2, 1.0);
|
||||||
|
|
||||||
|
info.data_in = (const float*)chunk->float_buf;
|
||||||
|
info.data_out = chunk->float_resample_buf;
|
||||||
|
/* a 'frame' consists of two channels, so we set this
|
||||||
|
* to the number of samples irrespective of channel count */
|
||||||
|
info.input_frames = chunk->rwav->numsamples;
|
||||||
|
info.output_frames = 0;
|
||||||
|
info.ratio = chunk->ratio;
|
||||||
|
|
||||||
|
chunk->resampler->process(chunk->resampler_data, &info);
|
||||||
|
|
||||||
|
/* number of output_frames does not increase with multiple channels, but assume we need space for 2 */
|
||||||
|
chunk->resample_buf = (int16_t*)memalign_alloc(128, info.output_frames * 2 * sizeof(int16_t));
|
||||||
|
chunk->resample_len = info.output_frames;
|
||||||
|
convert_float_to_s16(chunk->resample_buf, chunk->float_resample_buf, info.output_frames * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
|
||||||
|
error:
|
||||||
|
audio_mix_free_chunk(chunk);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk)
|
||||||
|
{
|
||||||
|
if (!chunk)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (chunk->rwav)
|
||||||
|
{
|
||||||
|
if (chunk->resample)
|
||||||
|
return chunk->resample_len;
|
||||||
|
return chunk->rwav->numsamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no other filetypes supported yet */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* audio_mix_get_chunk_sample:
|
||||||
|
* @chunk : audio chunk instance
|
||||||
|
* @channel : channel of the sample (0=left, 1=right)
|
||||||
|
* @index : index of the sample
|
||||||
|
*
|
||||||
|
* Get a sample from an audio chunk.
|
||||||
|
*
|
||||||
|
* Returns: A signed 16-bit audio sample.
|
||||||
|
**/
|
||||||
|
int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk, unsigned channel, size_t index)
|
||||||
|
{
|
||||||
|
if (!chunk)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (chunk->rwav)
|
||||||
|
{
|
||||||
|
int sample_size = chunk->rwav->bitspersample / 8;
|
||||||
|
int16_t sample_out = 0;
|
||||||
|
|
||||||
|
/* 0 is the first/left channel */
|
||||||
|
uint8_t *sample = NULL;
|
||||||
|
|
||||||
|
if (chunk->resample)
|
||||||
|
sample = (uint8_t*)chunk->resample_buf +
|
||||||
|
(sample_size * index * chunk->rwav->numchannels) + (channel * sample_size);
|
||||||
|
else
|
||||||
|
sample = (uint8_t*)chunk->upsample_buf +
|
||||||
|
(sample_size * index * chunk->rwav->numchannels) + (channel * sample_size);
|
||||||
|
|
||||||
|
sample_out = (int16_t)*sample;
|
||||||
|
|
||||||
|
return sample_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no other filetypes supported yet */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk)
|
||||||
|
{
|
||||||
|
if (!chunk)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (chunk->rwav)
|
||||||
|
{
|
||||||
|
int16_t *sample;
|
||||||
|
|
||||||
|
if (chunk->resample)
|
||||||
|
sample = chunk->resample_buf;
|
||||||
|
else
|
||||||
|
sample = chunk->upsample_buf;
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk)
|
||||||
|
{
|
||||||
|
if (!chunk)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (chunk->rwav)
|
||||||
|
return chunk->rwav->numchannels;
|
||||||
|
|
||||||
|
/* don't support other formats yet */
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,855 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (audio_mixer.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <audio/audio_mixer.h>
|
||||||
|
#include <audio/audio_resampler.h>
|
||||||
|
|
||||||
|
#include <formats/rwav.h>
|
||||||
|
#include <memalign.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
#include <rthreads/rthreads.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "../../config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
#define STB_VORBIS_NO_PUSHDATA_API
|
||||||
|
#define STB_VORBIS_NO_STDIO
|
||||||
|
#define STB_VORBIS_NO_CRT
|
||||||
|
|
||||||
|
#include <stb_vorbis.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
#include <ibxm/ibxm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define AUDIO_MIXER_MAX_VOICES 8
|
||||||
|
#define AUDIO_MIXER_TEMP_OGG_BUFFER 8192
|
||||||
|
|
||||||
|
struct audio_mixer_sound
|
||||||
|
{
|
||||||
|
enum audio_mixer_type type;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
/* wav */
|
||||||
|
unsigned frames;
|
||||||
|
const float* pcm;
|
||||||
|
} wav;
|
||||||
|
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
/* ogg */
|
||||||
|
unsigned size;
|
||||||
|
const void* data;
|
||||||
|
} ogg;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
/* mod/s3m/xm */
|
||||||
|
unsigned size;
|
||||||
|
const void* data;
|
||||||
|
} mod;
|
||||||
|
#endif
|
||||||
|
} types;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct audio_mixer_voice
|
||||||
|
{
|
||||||
|
bool repeat;
|
||||||
|
unsigned type;
|
||||||
|
float volume;
|
||||||
|
audio_mixer_sound_t *sound;
|
||||||
|
audio_mixer_stop_cb_t stop_cb;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned position;
|
||||||
|
} wav;
|
||||||
|
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned position;
|
||||||
|
unsigned samples;
|
||||||
|
unsigned buf_samples;
|
||||||
|
float* buffer;
|
||||||
|
float ratio;
|
||||||
|
stb_vorbis *stream;
|
||||||
|
void *resampler_data;
|
||||||
|
const retro_resampler_t *resampler;
|
||||||
|
} ogg;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned position;
|
||||||
|
unsigned samples;
|
||||||
|
unsigned buf_samples;
|
||||||
|
int* buffer;
|
||||||
|
struct replay* stream;
|
||||||
|
} mod;
|
||||||
|
#endif
|
||||||
|
} types;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct audio_mixer_voice s_voices[AUDIO_MIXER_MAX_VOICES];
|
||||||
|
static unsigned s_rate = 0;
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
static slock_t* s_locker = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool wav2float(const rwav_t* wav, float** pcm, size_t samples_out)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
/* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes */
|
||||||
|
float *f = (float*)memalign_alloc(16,
|
||||||
|
((samples_out + 15) & ~15) * sizeof(float));
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*pcm = f;
|
||||||
|
|
||||||
|
if (wav->bitspersample == 8)
|
||||||
|
{
|
||||||
|
float sample = 0.0f;
|
||||||
|
const uint8_t *u8 = (const uint8_t*)wav->samples;
|
||||||
|
|
||||||
|
if (wav->numchannels == 1)
|
||||||
|
{
|
||||||
|
for (i = wav->numsamples; i != 0; i--)
|
||||||
|
{
|
||||||
|
sample = (float)*u8++ / 255.0f;
|
||||||
|
sample = sample * 2.0f - 1.0f;
|
||||||
|
*f++ = sample;
|
||||||
|
*f++ = sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (wav->numchannels == 2)
|
||||||
|
{
|
||||||
|
for (i = wav->numsamples; i != 0; i--)
|
||||||
|
{
|
||||||
|
sample = (float)*u8++ / 255.0f;
|
||||||
|
sample = sample * 2.0f - 1.0f;
|
||||||
|
*f++ = sample;
|
||||||
|
sample = (float)*u8++ / 255.0f;
|
||||||
|
sample = sample * 2.0f - 1.0f;
|
||||||
|
*f++ = sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* TODO/FIXME note to leiradel - can we use audio/conversion/s16_to_float
|
||||||
|
* functions here? */
|
||||||
|
|
||||||
|
float sample = 0.0f;
|
||||||
|
const int16_t *s16 = (const int16_t*)wav->samples;
|
||||||
|
|
||||||
|
if (wav->numchannels == 1)
|
||||||
|
{
|
||||||
|
for (i = wav->numsamples; i != 0; i--)
|
||||||
|
{
|
||||||
|
sample = (float)((int)*s16++ + 32768) / 65535.0f;
|
||||||
|
sample = sample * 2.0f - 1.0f;
|
||||||
|
*f++ = sample;
|
||||||
|
*f++ = sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (wav->numchannels == 2)
|
||||||
|
{
|
||||||
|
for (i = wav->numsamples; i != 0; i--)
|
||||||
|
{
|
||||||
|
sample = (float)((int)*s16++ + 32768) / 65535.0f;
|
||||||
|
sample = sample * 2.0f - 1.0f;
|
||||||
|
*f++ = sample;
|
||||||
|
sample = (float)((int)*s16++ + 32768) / 65535.0f;
|
||||||
|
sample = sample * 2.0f - 1.0f;
|
||||||
|
*f++ = sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool one_shot_resample(const float* in, size_t samples_in,
|
||||||
|
unsigned rate, float** out, size_t* samples_out)
|
||||||
|
{
|
||||||
|
struct resampler_data info;
|
||||||
|
void* data = NULL;
|
||||||
|
const retro_resampler_t* resampler = NULL;
|
||||||
|
float ratio = (double)s_rate / (double)rate;
|
||||||
|
|
||||||
|
if (!retro_resampler_realloc(&data, &resampler, NULL,
|
||||||
|
RESAMPLER_QUALITY_DONTCARE, ratio))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes. We
|
||||||
|
* add four more samples in the formula below just as safeguard, because
|
||||||
|
* resampler->process sometimes reports more output samples than the
|
||||||
|
* formula below calculates. Ideally, audio resamplers should have a
|
||||||
|
* function to return the number of samples they will output given a
|
||||||
|
* count of input samples.
|
||||||
|
*/
|
||||||
|
*samples_out = samples_in * ratio + 4;
|
||||||
|
*out = (float*)memalign_alloc(16,
|
||||||
|
((*samples_out + 15) & ~15) * sizeof(float));
|
||||||
|
|
||||||
|
if (*out == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
info.data_in = in;
|
||||||
|
info.data_out = *out;
|
||||||
|
info.input_frames = samples_in / 2;
|
||||||
|
info.output_frames = 0;
|
||||||
|
info.ratio = ratio;
|
||||||
|
|
||||||
|
resampler->process(data, &info);
|
||||||
|
resampler->free(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_mixer_init(unsigned rate)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
s_rate = rate;
|
||||||
|
|
||||||
|
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++)
|
||||||
|
s_voices[i].type = AUDIO_MIXER_TYPE_NONE;
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
s_locker = slock_new();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_mixer_done(void)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
/* Dont call audio mixer functions after this point */
|
||||||
|
slock_free(s_locker);
|
||||||
|
s_locker = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++)
|
||||||
|
s_voices[i].type = AUDIO_MIXER_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size)
|
||||||
|
{
|
||||||
|
/* WAV data */
|
||||||
|
rwav_t wav;
|
||||||
|
/* WAV samples converted to float */
|
||||||
|
float* pcm = NULL;
|
||||||
|
size_t samples = 0;
|
||||||
|
/* Result */
|
||||||
|
audio_mixer_sound_t* sound = NULL;
|
||||||
|
enum rwav_state rwav_ret = rwav_load(&wav, buffer, size);
|
||||||
|
|
||||||
|
if (rwav_ret != RWAV_ITERATE_DONE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
samples = wav.numsamples * 2;
|
||||||
|
|
||||||
|
if (!wav2float(&wav, &pcm, samples))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (wav.samplerate != s_rate)
|
||||||
|
{
|
||||||
|
float* resampled = NULL;
|
||||||
|
|
||||||
|
if (!one_shot_resample(pcm, samples,
|
||||||
|
wav.samplerate, &resampled, &samples))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memalign_free((void*)pcm);
|
||||||
|
pcm = resampled;
|
||||||
|
}
|
||||||
|
|
||||||
|
sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
|
||||||
|
|
||||||
|
if (!sound)
|
||||||
|
{
|
||||||
|
memalign_free((void*)pcm);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sound->type = AUDIO_MIXER_TYPE_WAV;
|
||||||
|
sound->types.wav.frames = (unsigned)(samples / 2);
|
||||||
|
sound->types.wav.pcm = pcm;
|
||||||
|
|
||||||
|
rwav_free(&wav);
|
||||||
|
|
||||||
|
return sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
|
||||||
|
|
||||||
|
if (!sound)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sound->type = AUDIO_MIXER_TYPE_OGG;
|
||||||
|
sound->types.ogg.size = size;
|
||||||
|
sound->types.ogg.data = buffer;
|
||||||
|
|
||||||
|
return sound;
|
||||||
|
#else
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
|
||||||
|
|
||||||
|
if (!sound)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sound->type = AUDIO_MIXER_TYPE_MOD;
|
||||||
|
sound->types.mod.size = size;
|
||||||
|
sound->types.mod.data = buffer;
|
||||||
|
|
||||||
|
return sound;
|
||||||
|
#else
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_mixer_destroy(audio_mixer_sound_t* sound)
|
||||||
|
{
|
||||||
|
void *handle = NULL;
|
||||||
|
if (!sound)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (sound->type)
|
||||||
|
{
|
||||||
|
case AUDIO_MIXER_TYPE_WAV:
|
||||||
|
handle = (void*)sound->types.wav.pcm;
|
||||||
|
if (handle)
|
||||||
|
memalign_free(handle);
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_OGG:
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
handle = (void*)sound->types.ogg.data;
|
||||||
|
if (handle)
|
||||||
|
free(handle);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_MOD:
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
handle = (void*)sound->types.mod.data;
|
||||||
|
if (handle)
|
||||||
|
free(handle);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sound);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool audio_mixer_play_wav(audio_mixer_sound_t* sound,
|
||||||
|
audio_mixer_voice_t* voice, bool repeat, float volume,
|
||||||
|
audio_mixer_stop_cb_t stop_cb)
|
||||||
|
{
|
||||||
|
voice->types.wav.position = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
static bool audio_mixer_play_ogg(
|
||||||
|
audio_mixer_sound_t* sound,
|
||||||
|
audio_mixer_voice_t* voice,
|
||||||
|
bool repeat, float volume,
|
||||||
|
audio_mixer_stop_cb_t stop_cb)
|
||||||
|
{
|
||||||
|
stb_vorbis_info info;
|
||||||
|
int res = 0;
|
||||||
|
float ratio = 0.0f;
|
||||||
|
unsigned samples = 0;
|
||||||
|
void *ogg_buffer = NULL;
|
||||||
|
void *resampler_data = NULL;
|
||||||
|
const retro_resampler_t* resamp = NULL;
|
||||||
|
stb_vorbis *stb_vorbis = stb_vorbis_open_memory(
|
||||||
|
(const unsigned char*)sound->types.ogg.data,
|
||||||
|
sound->types.ogg.size, &res, NULL);
|
||||||
|
|
||||||
|
if (!stb_vorbis)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
info = stb_vorbis_get_info(stb_vorbis);
|
||||||
|
|
||||||
|
if (info.sample_rate != s_rate)
|
||||||
|
{
|
||||||
|
ratio = (double)s_rate / (double)info.sample_rate;
|
||||||
|
|
||||||
|
if (!retro_resampler_realloc(&resampler_data,
|
||||||
|
&resamp, NULL, RESAMPLER_QUALITY_DONTCARE,
|
||||||
|
ratio))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = (unsigned)(AUDIO_MIXER_TEMP_OGG_BUFFER * ratio);
|
||||||
|
ogg_buffer = (float*)memalign_alloc(16,
|
||||||
|
((samples + 15) & ~15) * sizeof(float));
|
||||||
|
|
||||||
|
if (!ogg_buffer)
|
||||||
|
{
|
||||||
|
resamp->free(resampler_data);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice->types.ogg.resampler = resamp;
|
||||||
|
voice->types.ogg.resampler_data = resampler_data;
|
||||||
|
voice->types.ogg.buffer = (float*)ogg_buffer;
|
||||||
|
voice->types.ogg.buf_samples = samples;
|
||||||
|
voice->types.ogg.ratio = ratio;
|
||||||
|
voice->types.ogg.stream = stb_vorbis;
|
||||||
|
voice->types.ogg.position = 0;
|
||||||
|
voice->types.ogg.samples = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
stb_vorbis_close(stb_vorbis);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
static bool audio_mixer_play_mod(
|
||||||
|
audio_mixer_sound_t* sound,
|
||||||
|
audio_mixer_voice_t* voice,
|
||||||
|
bool repeat, float volume,
|
||||||
|
audio_mixer_stop_cb_t stop_cb)
|
||||||
|
{
|
||||||
|
struct data data;
|
||||||
|
char message[64];
|
||||||
|
int buf_samples = 0;
|
||||||
|
int samples = 0;
|
||||||
|
void *mod_buffer = NULL;
|
||||||
|
struct module* module = NULL;
|
||||||
|
struct replay* replay = NULL;
|
||||||
|
|
||||||
|
data.buffer = (char*)sound->types.mod.data;
|
||||||
|
data.length = sound->types.mod.size;
|
||||||
|
module = module_load(&data, message);
|
||||||
|
|
||||||
|
if (!module)
|
||||||
|
{
|
||||||
|
printf("audio_mixer_play_mod module_load() failed with error: %s\n", message);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
replay = new_replay( module, s_rate, 1);
|
||||||
|
|
||||||
|
if (!replay)
|
||||||
|
{
|
||||||
|
printf("audio_mixer_play_mod new_replay() failed\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_samples = calculate_mix_buf_len(s_rate);
|
||||||
|
mod_buffer = memalign_alloc(16, ((buf_samples + 15) & ~15) * sizeof(int));
|
||||||
|
|
||||||
|
if (!mod_buffer)
|
||||||
|
{
|
||||||
|
printf("audio_mixer_play_mod cannot allocate mod_buffer !\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = replay_calculate_duration(replay);
|
||||||
|
|
||||||
|
if (!samples)
|
||||||
|
{
|
||||||
|
printf("audio_mixer_play_mod cannot retrieve duration !\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice->types.mod.buffer = (int*)mod_buffer;
|
||||||
|
voice->types.mod.buf_samples = buf_samples;
|
||||||
|
voice->types.mod.stream = replay;
|
||||||
|
voice->types.mod.position = 0;
|
||||||
|
voice->types.mod.samples = 0; /* samples; */
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (mod_buffer)
|
||||||
|
memalign_free(mod_buffer);
|
||||||
|
if (module)
|
||||||
|
dispose_module(module);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat,
|
||||||
|
float volume, audio_mixer_stop_cb_t stop_cb)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
bool res = false;
|
||||||
|
audio_mixer_voice_t* voice = s_voices;
|
||||||
|
|
||||||
|
if (!sound)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
slock_lock(s_locker);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++, voice++)
|
||||||
|
{
|
||||||
|
if (voice->type != AUDIO_MIXER_TYPE_NONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (sound->type)
|
||||||
|
{
|
||||||
|
case AUDIO_MIXER_TYPE_WAV:
|
||||||
|
res = audio_mixer_play_wav(sound, voice, repeat, volume, stop_cb);
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_OGG:
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
res = audio_mixer_play_ogg(sound, voice, repeat, volume, stop_cb);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_MOD:
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
res = audio_mixer_play_mod(sound, voice, repeat, volume, stop_cb);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
voice->type = sound->type;
|
||||||
|
voice->repeat = repeat;
|
||||||
|
voice->volume = volume;
|
||||||
|
voice->sound = sound;
|
||||||
|
voice->stop_cb = stop_cb;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
voice = NULL;
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
slock_unlock(s_locker);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return voice;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_mixer_stop(audio_mixer_voice_t* voice)
|
||||||
|
{
|
||||||
|
audio_mixer_stop_cb_t stop_cb = NULL;
|
||||||
|
audio_mixer_sound_t* sound = NULL;
|
||||||
|
|
||||||
|
if (voice)
|
||||||
|
{
|
||||||
|
stop_cb = voice->stop_cb;
|
||||||
|
sound = voice->sound;
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
slock_lock(s_locker);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
voice->type = AUDIO_MIXER_TYPE_NONE;
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
slock_unlock(s_locker);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (stop_cb)
|
||||||
|
stop_cb(sound, AUDIO_MIXER_SOUND_STOPPED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audio_mixer_mix_wav(float* buffer, size_t num_frames,
|
||||||
|
audio_mixer_voice_t* voice,
|
||||||
|
float volume)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned buf_free = (unsigned)(num_frames * 2);
|
||||||
|
const audio_mixer_sound_t* sound = voice->sound;
|
||||||
|
unsigned pcm_available = sound->types.wav.frames
|
||||||
|
* 2 - voice->types.wav.position;
|
||||||
|
const float* pcm = sound->types.wav.pcm +
|
||||||
|
voice->types.wav.position;
|
||||||
|
|
||||||
|
again:
|
||||||
|
if (pcm_available < buf_free)
|
||||||
|
{
|
||||||
|
for (i = pcm_available; i != 0; i--)
|
||||||
|
*buffer++ += *pcm++ * volume;
|
||||||
|
|
||||||
|
if (voice->repeat)
|
||||||
|
{
|
||||||
|
if (voice->stop_cb)
|
||||||
|
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);
|
||||||
|
|
||||||
|
buf_free -= pcm_available;
|
||||||
|
pcm_available = sound->types.wav.frames * 2;
|
||||||
|
pcm = sound->types.wav.pcm;
|
||||||
|
voice->types.wav.position = 0;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voice->stop_cb)
|
||||||
|
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
|
||||||
|
|
||||||
|
voice->type = AUDIO_MIXER_TYPE_NONE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = buf_free; i != 0; i--)
|
||||||
|
*buffer++ += *pcm++ * volume;
|
||||||
|
|
||||||
|
voice->types.wav.position += buf_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
static void audio_mixer_mix_ogg(float* buffer, size_t num_frames,
|
||||||
|
audio_mixer_voice_t* voice,
|
||||||
|
float volume)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct resampler_data info;
|
||||||
|
float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER];
|
||||||
|
unsigned buf_free = num_frames * 2;
|
||||||
|
unsigned temp_samples = 0;
|
||||||
|
float* pcm = NULL;
|
||||||
|
|
||||||
|
if (voice->types.ogg.position == voice->types.ogg.samples)
|
||||||
|
{
|
||||||
|
again:
|
||||||
|
temp_samples = stb_vorbis_get_samples_float_interleaved(
|
||||||
|
voice->types.ogg.stream, 2, temp_buffer,
|
||||||
|
AUDIO_MIXER_TEMP_OGG_BUFFER) * 2;
|
||||||
|
|
||||||
|
if (temp_samples == 0)
|
||||||
|
{
|
||||||
|
if (voice->repeat)
|
||||||
|
{
|
||||||
|
if (voice->stop_cb)
|
||||||
|
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);
|
||||||
|
|
||||||
|
stb_vorbis_seek_start(voice->types.ogg.stream);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (voice->stop_cb)
|
||||||
|
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
|
||||||
|
|
||||||
|
voice->type = AUDIO_MIXER_TYPE_NONE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info.data_in = temp_buffer;
|
||||||
|
info.data_out = voice->types.ogg.buffer;
|
||||||
|
info.input_frames = temp_samples / 2;
|
||||||
|
info.output_frames = 0;
|
||||||
|
info.ratio = voice->types.ogg.ratio;
|
||||||
|
|
||||||
|
voice->types.ogg.resampler->process(voice->types.ogg.resampler_data, &info);
|
||||||
|
voice->types.ogg.position = 0;
|
||||||
|
voice->types.ogg.samples = voice->types.ogg.buf_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcm = voice->types.ogg.buffer + voice->types.ogg.position;
|
||||||
|
|
||||||
|
if (voice->types.ogg.samples < buf_free)
|
||||||
|
{
|
||||||
|
for (i = voice->types.ogg.samples; i != 0; i--)
|
||||||
|
*buffer++ += *pcm++ * volume;
|
||||||
|
|
||||||
|
buf_free -= voice->types.ogg.samples;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = buf_free; i != 0; --i )
|
||||||
|
*buffer++ += *pcm++ * volume;
|
||||||
|
|
||||||
|
voice->types.ogg.position += buf_free;
|
||||||
|
voice->types.ogg.samples -= buf_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
static void audio_mixer_mix_mod(float* buffer, size_t num_frames,
|
||||||
|
audio_mixer_voice_t* voice,
|
||||||
|
float volume)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float samplef = 0.0f;
|
||||||
|
int samplei = 0;
|
||||||
|
unsigned temp_samples = 0;
|
||||||
|
unsigned buf_free = num_frames * 2;
|
||||||
|
int* pcm = NULL;
|
||||||
|
|
||||||
|
if (voice->types.mod.position == voice->types.mod.samples)
|
||||||
|
{
|
||||||
|
again:
|
||||||
|
temp_samples = replay_get_audio(
|
||||||
|
voice->types.mod.stream, voice->types.mod.buffer );
|
||||||
|
|
||||||
|
temp_samples *= 2; /* stereo */
|
||||||
|
|
||||||
|
if (temp_samples == 0)
|
||||||
|
{
|
||||||
|
if (voice->repeat)
|
||||||
|
{
|
||||||
|
if (voice->stop_cb)
|
||||||
|
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);
|
||||||
|
|
||||||
|
replay_seek( voice->types.mod.stream, 0);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (voice->stop_cb)
|
||||||
|
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
|
||||||
|
|
||||||
|
voice->type = AUDIO_MIXER_TYPE_NONE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voice->types.mod.position = 0;
|
||||||
|
voice->types.mod.samples = temp_samples;
|
||||||
|
}
|
||||||
|
pcm = voice->types.mod.buffer + voice->types.mod.position;
|
||||||
|
|
||||||
|
if (voice->types.mod.samples < buf_free)
|
||||||
|
{
|
||||||
|
for (i = voice->types.mod.samples; i != 0; i--)
|
||||||
|
{
|
||||||
|
samplei = *pcm++ * volume;
|
||||||
|
samplef = (float)((int)samplei + 32768) / 65535.0f;
|
||||||
|
samplef = samplef * 2.0f - 1.0f;
|
||||||
|
*buffer++ += samplef;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_free -= voice->types.mod.samples;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = buf_free; i != 0; --i )
|
||||||
|
{
|
||||||
|
samplei = *pcm++ * volume;
|
||||||
|
samplef = (float)((int)samplei + 32768) / 65535.0f;
|
||||||
|
samplef = samplef * 2.0f - 1.0f;
|
||||||
|
*buffer++ += samplef;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice->types.mod.position += buf_free;
|
||||||
|
voice->types.mod.samples -= buf_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bool override)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
size_t j = 0;
|
||||||
|
float* sample = NULL;
|
||||||
|
audio_mixer_voice_t* voice = s_voices;
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
slock_lock(s_locker);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++, voice++)
|
||||||
|
{
|
||||||
|
float volume = (override) ? volume_override : voice->volume;
|
||||||
|
|
||||||
|
switch (voice->type)
|
||||||
|
{
|
||||||
|
case AUDIO_MIXER_TYPE_WAV:
|
||||||
|
audio_mixer_mix_wav(buffer, num_frames, voice, volume);
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_OGG:
|
||||||
|
#ifdef HAVE_STB_VORBIS
|
||||||
|
audio_mixer_mix_ogg(buffer, num_frames, voice, volume);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_MOD:
|
||||||
|
#ifdef HAVE_IBXM
|
||||||
|
audio_mixer_mix_mod(buffer, num_frames, voice, volume);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case AUDIO_MIXER_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
slock_unlock(s_locker);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (j = 0, sample = buffer; j < num_frames; j++, sample++)
|
||||||
|
{
|
||||||
|
if (*sample < -1.0f)
|
||||||
|
*sample = -1.0f;
|
||||||
|
else if (*sample > 1.0f)
|
||||||
|
*sample = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (float_to_s16.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#elif defined(__ALTIVEC__)
|
||||||
|
#include <altivec.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <features/features_cpu.h>
|
||||||
|
#include <audio/conversion/float_to_s16.h>
|
||||||
|
|
||||||
|
#if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||||
|
static bool float_to_s16_neon_enabled = false;
|
||||||
|
void convert_float_s16_asm(int16_t *out, const float *in, size_t samples);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert_float_to_s16:
|
||||||
|
* @out : output buffer
|
||||||
|
* @in : input buffer
|
||||||
|
* @samples : size of samples to be converted
|
||||||
|
*
|
||||||
|
* Converts floating point
|
||||||
|
* to signed integer 16-bit.
|
||||||
|
*
|
||||||
|
* C implementation callback function.
|
||||||
|
**/
|
||||||
|
void convert_float_to_s16(int16_t *out,
|
||||||
|
const float *in, size_t samples)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
__m128 factor = _mm_set1_ps((float)0x8000);
|
||||||
|
|
||||||
|
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||||
|
{
|
||||||
|
__m128 input_l = _mm_loadu_ps(in + 0);
|
||||||
|
__m128 input_r = _mm_loadu_ps(in + 4);
|
||||||
|
__m128 res_l = _mm_mul_ps(input_l, factor);
|
||||||
|
__m128 res_r = _mm_mul_ps(input_r, factor);
|
||||||
|
__m128i ints_l = _mm_cvtps_epi32(res_l);
|
||||||
|
__m128i ints_r = _mm_cvtps_epi32(res_r);
|
||||||
|
__m128i packed = _mm_packs_epi32(ints_l, ints_r);
|
||||||
|
|
||||||
|
_mm_storeu_si128((__m128i *)out, packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = samples - i;
|
||||||
|
i = 0;
|
||||||
|
#elif defined(__ALTIVEC__)
|
||||||
|
int samples_in = samples;
|
||||||
|
|
||||||
|
/* Unaligned loads/store is a bit expensive,
|
||||||
|
* so we optimize for the good path (very likely). */
|
||||||
|
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||||
|
{
|
||||||
|
vector float input0 = vec_ld( 0, in);
|
||||||
|
vector float input1 = vec_ld(16, in);
|
||||||
|
vector signed int result0 = vec_cts(input0, 15);
|
||||||
|
vector signed int result1 = vec_cts(input1, 15);
|
||||||
|
vec_st(vec_packs(result0, result1), 0, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
samples_in -= i;
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = samples_in;
|
||||||
|
i = 0;
|
||||||
|
#elif defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||||
|
if (float_to_s16_neon_enabled)
|
||||||
|
{
|
||||||
|
size_t aligned_samples = samples & ~7;
|
||||||
|
if (aligned_samples)
|
||||||
|
convert_float_s16_asm(out, in, aligned_samples);
|
||||||
|
|
||||||
|
out = out + aligned_samples;
|
||||||
|
in = in + aligned_samples;
|
||||||
|
samples = samples - aligned_samples;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
#elif defined(_MIPS_ARCH_ALLEGREX)
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
/* Make sure the buffers are 16 byte aligned, this should be
|
||||||
|
* the default behaviour of malloc in the PSPSDK.
|
||||||
|
* Assume alignment. */
|
||||||
|
retro_assert(((uintptr_t)in & 0xf) == 0);
|
||||||
|
retro_assert(((uintptr_t)out & 0xf) == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i = 0; i + 8 <= samples; i += 8)
|
||||||
|
{
|
||||||
|
__asm__ (
|
||||||
|
".set push \n"
|
||||||
|
".set noreorder \n"
|
||||||
|
|
||||||
|
"lv.q c100, 0(%0) \n"
|
||||||
|
"lv.q c110, 16(%0) \n"
|
||||||
|
|
||||||
|
"vf2in.q c100, c100, 31 \n"
|
||||||
|
"vf2in.q c110, c110, 31 \n"
|
||||||
|
"vi2s.q c100, c100 \n"
|
||||||
|
"vi2s.q c102, c110 \n"
|
||||||
|
|
||||||
|
"sv.q c100, 0(%1) \n"
|
||||||
|
|
||||||
|
".set pop \n"
|
||||||
|
:: "r"(in + i), "r"(out + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; i < samples; i++)
|
||||||
|
{
|
||||||
|
int32_t val = (int32_t)(in[i] * 0x8000);
|
||||||
|
out[i] = (val > 0x7FFF) ? 0x7FFF :
|
||||||
|
(val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert_float_to_s16_init_simd:
|
||||||
|
*
|
||||||
|
* Sets up function pointers for conversion
|
||||||
|
* functions based on CPU features.
|
||||||
|
**/
|
||||||
|
void convert_float_to_s16_init_simd(void)
|
||||||
|
{
|
||||||
|
#if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||||
|
unsigned cpu = cpu_features_get();
|
||||||
|
|
||||||
|
if (cpu & RETRO_SIMD_NEON)
|
||||||
|
float_to_s16_neon_enabled = true;
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (float_to_s16_neon.S).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if defined(__ARM_NEON__)
|
||||||
|
|
||||||
|
#ifndef __MACH__
|
||||||
|
.arm
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.align 4
|
||||||
|
.globl convert_float_s16_asm
|
||||||
|
#ifndef __MACH__
|
||||||
|
.type convert_float_s16_asm, %function
|
||||||
|
#endif
|
||||||
|
.globl _convert_float_s16_asm
|
||||||
|
#ifndef __MACH__
|
||||||
|
.type _convert_float_s16_asm, %function
|
||||||
|
#endif
|
||||||
|
# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)
|
||||||
|
convert_float_s16_asm:
|
||||||
|
_convert_float_s16_asm:
|
||||||
|
# Hacky way to get a constant of 2^15.
|
||||||
|
# ((2^4)^2)^2 * 0.5 = 2^15
|
||||||
|
vmov.f32 q8, #16.0
|
||||||
|
vmov.f32 q9, #0.5
|
||||||
|
vmul.f32 q8, q8, q8
|
||||||
|
vmul.f32 q8, q8, q8
|
||||||
|
vmul.f32 q8, q8, q9
|
||||||
|
|
||||||
|
1:
|
||||||
|
# Preload here?
|
||||||
|
vld1.f32 {q0-q1}, [r1]!
|
||||||
|
|
||||||
|
vmul.f32 q0, q0, q8
|
||||||
|
vmul.f32 q1, q1, q8
|
||||||
|
|
||||||
|
vcvt.s32.f32 q0, q0
|
||||||
|
vcvt.s32.f32 q1, q1
|
||||||
|
|
||||||
|
vqmovn.s32 d4, q0
|
||||||
|
vqmovn.s32 d5, q1
|
||||||
|
|
||||||
|
vst1.f32 {d4-d5}, [r0]!
|
||||||
|
|
||||||
|
# Guaranteed to get samples in multiples of 8.
|
||||||
|
subs r2, r2, #8
|
||||||
|
bne 1b
|
||||||
|
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (float_to_s16_neon.S).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if defined(__ARM_NEON__)
|
||||||
|
|
||||||
|
#if defined(__thumb__)
|
||||||
|
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
|
||||||
|
#else
|
||||||
|
#define DECL_ARMMODE(x) " .align 4\n" " .global " x "\n" " .arm\n" x ":\n"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
asm(
|
||||||
|
DECL_ARMMODE("convert_float_s16_asm")
|
||||||
|
DECL_ARMMODE("_convert_float_s16_asm")
|
||||||
|
"# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)\n"
|
||||||
|
" # Hacky way to get a constant of 2^15.\n"
|
||||||
|
" # ((2^4)^2)^2 * 0.5 = 2^15\n"
|
||||||
|
" vmov.f32 q8, #16.0\n"
|
||||||
|
" vmov.f32 q9, #0.5\n"
|
||||||
|
" vmul.f32 q8, q8, q8\n"
|
||||||
|
" vmul.f32 q8, q8, q8\n"
|
||||||
|
" vmul.f32 q8, q8, q9\n"
|
||||||
|
"\n"
|
||||||
|
"1:\n"
|
||||||
|
" # Preload here?\n"
|
||||||
|
" vld1.f32 {q0-q1}, [r1]!\n"
|
||||||
|
"\n"
|
||||||
|
" vmul.f32 q0, q0, q8\n"
|
||||||
|
" vmul.f32 q1, q1, q8\n"
|
||||||
|
"\n"
|
||||||
|
" vcvt.s32.f32 q0, q0\n"
|
||||||
|
" vcvt.s32.f32 q1, q1\n"
|
||||||
|
"\n"
|
||||||
|
" vqmovn.s32 d4, q0\n"
|
||||||
|
" vqmovn.s32 d5, q1\n"
|
||||||
|
"\n"
|
||||||
|
" vst1.f32 {d4-d5}, [r0]!\n"
|
||||||
|
"\n"
|
||||||
|
" # Guaranteed to get samples in multiples of 8.\n"
|
||||||
|
" subs r2, r2, #8\n"
|
||||||
|
" bne 1b\n"
|
||||||
|
"\n"
|
||||||
|
" bx lr\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
#endif
|
|
@ -0,0 +1,189 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (s16_to_float.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#elif defined(__ALTIVEC__)
|
||||||
|
#include <altivec.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boolean.h>
|
||||||
|
#include <features/features_cpu.h>
|
||||||
|
#include <audio/conversion/s16_to_float.h>
|
||||||
|
|
||||||
|
#if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||||
|
static bool s16_to_float_neon_enabled = false;
|
||||||
|
|
||||||
|
/* Avoid potential hard-float/soft-float ABI issues. */
|
||||||
|
void convert_s16_float_asm(float *out, const int16_t *in,
|
||||||
|
size_t samples, const float *gain);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert_s16_to_float:
|
||||||
|
* @out : output buffer
|
||||||
|
* @in : input buffer
|
||||||
|
* @samples : size of samples to be converted
|
||||||
|
* @gain : gain applied (.e.g. audio volume)
|
||||||
|
*
|
||||||
|
* Converts from signed integer 16-bit
|
||||||
|
* to floating point.
|
||||||
|
**/
|
||||||
|
void convert_s16_to_float(float *out,
|
||||||
|
const int16_t *in, size_t samples, float gain)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
float fgain = gain / UINT32_C(0x80000000);
|
||||||
|
__m128 factor = _mm_set1_ps(fgain);
|
||||||
|
|
||||||
|
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||||
|
{
|
||||||
|
__m128i input = _mm_loadu_si128((const __m128i *)in);
|
||||||
|
__m128i regs_l = _mm_unpacklo_epi16(_mm_setzero_si128(), input);
|
||||||
|
__m128i regs_r = _mm_unpackhi_epi16(_mm_setzero_si128(), input);
|
||||||
|
__m128 output_l = _mm_mul_ps(_mm_cvtepi32_ps(regs_l), factor);
|
||||||
|
__m128 output_r = _mm_mul_ps(_mm_cvtepi32_ps(regs_r), factor);
|
||||||
|
|
||||||
|
_mm_storeu_ps(out + 0, output_l);
|
||||||
|
_mm_storeu_ps(out + 4, output_r);
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = samples - i;
|
||||||
|
i = 0;
|
||||||
|
#elif defined(__ALTIVEC__)
|
||||||
|
size_t samples_in = samples;
|
||||||
|
|
||||||
|
/* Unaligned loads/store is a bit expensive, so we
|
||||||
|
* optimize for the good path (very likely). */
|
||||||
|
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
const vector float gain_vec = { gain, gain , gain, gain };
|
||||||
|
const vector float zero_vec = { 0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
|
||||||
|
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||||
|
{
|
||||||
|
vector signed short input = vec_ld(0, in);
|
||||||
|
vector signed int hi = vec_unpackh(input);
|
||||||
|
vector signed int lo = vec_unpackl(input);
|
||||||
|
vector float out_hi = vec_madd(vec_ctf(hi, 15), gain_vec, zero_vec);
|
||||||
|
vector float out_lo = vec_madd(vec_ctf(lo, 15), gain_vec, zero_vec);
|
||||||
|
|
||||||
|
vec_st(out_hi, 0, out);
|
||||||
|
vec_st(out_lo, 16, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
samples_in -= i;
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = samples_in;
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
#elif defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||||
|
if (s16_to_float_neon_enabled)
|
||||||
|
{
|
||||||
|
size_t aligned_samples = samples & ~7;
|
||||||
|
if (aligned_samples)
|
||||||
|
convert_s16_float_asm(out, in, aligned_samples, &gain);
|
||||||
|
|
||||||
|
/* Could do all conversion in ASM, but keep it simple for now. */
|
||||||
|
out = out + aligned_samples;
|
||||||
|
in = in + aligned_samples;
|
||||||
|
samples = samples - aligned_samples;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(_MIPS_ARCH_ALLEGREX)
|
||||||
|
#ifdef DEBUG
|
||||||
|
/* Make sure the buffer is 16 byte aligned, this should be the
|
||||||
|
* default behaviour of malloc in the PSPSDK.
|
||||||
|
* Only the output buffer can be assumed to be 16-byte aligned. */
|
||||||
|
retro_assert(((uintptr_t)out & 0xf) == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gain = gain / 0x8000;
|
||||||
|
__asm__ (
|
||||||
|
".set push \n"
|
||||||
|
".set noreorder \n"
|
||||||
|
"mtv %0, s200 \n"
|
||||||
|
".set pop \n"
|
||||||
|
::"r"(gain));
|
||||||
|
|
||||||
|
for (i = 0; i + 16 <= samples; i += 16)
|
||||||
|
{
|
||||||
|
__asm__ (
|
||||||
|
".set push \n"
|
||||||
|
".set noreorder \n"
|
||||||
|
|
||||||
|
"lv.s s100, 0(%0) \n"
|
||||||
|
"lv.s s101, 4(%0) \n"
|
||||||
|
"lv.s s110, 8(%0) \n"
|
||||||
|
"lv.s s111, 12(%0) \n"
|
||||||
|
"lv.s s120, 16(%0) \n"
|
||||||
|
"lv.s s121, 20(%0) \n"
|
||||||
|
"lv.s s130, 24(%0) \n"
|
||||||
|
"lv.s s131, 28(%0) \n"
|
||||||
|
|
||||||
|
"vs2i.p c100, c100 \n"
|
||||||
|
"vs2i.p c110, c110 \n"
|
||||||
|
"vs2i.p c120, c120 \n"
|
||||||
|
"vs2i.p c130, c130 \n"
|
||||||
|
|
||||||
|
"vi2f.q c100, c100, 16 \n"
|
||||||
|
"vi2f.q c110, c110, 16 \n"
|
||||||
|
"vi2f.q c120, c120, 16 \n"
|
||||||
|
"vi2f.q c130, c130, 16 \n"
|
||||||
|
|
||||||
|
"vmscl.q e100, e100, s200 \n"
|
||||||
|
|
||||||
|
"sv.q c100, 0(%1) \n"
|
||||||
|
"sv.q c110, 16(%1) \n"
|
||||||
|
"sv.q c120, 32(%1) \n"
|
||||||
|
"sv.q c130, 48(%1) \n"
|
||||||
|
|
||||||
|
".set pop \n"
|
||||||
|
:: "r"(in + i), "r"(out + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
gain = gain / 0x8000;
|
||||||
|
|
||||||
|
for (; i < samples; i++)
|
||||||
|
out[i] = (float)in[i] * gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert_s16_to_float_init_simd:
|
||||||
|
*
|
||||||
|
* Sets up function pointers for conversion
|
||||||
|
* functions based on CPU features.
|
||||||
|
**/
|
||||||
|
void convert_s16_to_float_init_simd(void)
|
||||||
|
{
|
||||||
|
#if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||||
|
unsigned cpu = cpu_features_get();
|
||||||
|
|
||||||
|
if (cpu & RETRO_SIMD_NEON)
|
||||||
|
s16_to_float_neon_enabled = true;
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (s16_to_float_neon.S).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if defined(__ARM_NEON__)
|
||||||
|
|
||||||
|
#ifndef __MACH__
|
||||||
|
.arm
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.align 4
|
||||||
|
.globl convert_s16_float_asm
|
||||||
|
#ifndef __MACH__
|
||||||
|
.type convert_s16_float_asm, %function
|
||||||
|
#endif
|
||||||
|
.globl _convert_s16_float_asm
|
||||||
|
#ifndef __MACH__
|
||||||
|
.type _convert_s16_float_asm, %function
|
||||||
|
#endif
|
||||||
|
# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)
|
||||||
|
convert_s16_float_asm:
|
||||||
|
_convert_s16_float_asm:
|
||||||
|
# Hacky way to get a constant of 2^-15.
|
||||||
|
# Might be faster to just load a constant from memory.
|
||||||
|
# It's just done once however ...
|
||||||
|
vmov.f32 q8, #0.25
|
||||||
|
vmul.f32 q8, q8, q8
|
||||||
|
vmul.f32 q8, q8, q8
|
||||||
|
vmul.f32 q8, q8, q8
|
||||||
|
vadd.f32 q8, q8, q8
|
||||||
|
|
||||||
|
# Apply gain
|
||||||
|
vld1.f32 {d6[0]}, [r3]
|
||||||
|
vmul.f32 q8, q8, d6[0]
|
||||||
|
|
||||||
|
1:
|
||||||
|
# Preload here?
|
||||||
|
vld1.s16 {q0}, [r1]!
|
||||||
|
|
||||||
|
# Widen to 32-bit
|
||||||
|
vmovl.s16 q1, d0
|
||||||
|
vmovl.s16 q2, d1
|
||||||
|
|
||||||
|
# Convert to float
|
||||||
|
vcvt.f32.s32 q1, q1
|
||||||
|
vcvt.f32.s32 q2, q2
|
||||||
|
|
||||||
|
vmul.f32 q1, q1, q8
|
||||||
|
vmul.f32 q2, q2, q8
|
||||||
|
|
||||||
|
vst1.f32 {q1-q2}, [r0]!
|
||||||
|
|
||||||
|
# Guaranteed to get samples in multiples of 8.
|
||||||
|
subs r2, r2, #8
|
||||||
|
bne 1b
|
||||||
|
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (s16_to_float_neon.S).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if defined(__ARM_NEON__)
|
||||||
|
|
||||||
|
#if defined(__thumb__)
|
||||||
|
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
|
||||||
|
#else
|
||||||
|
#define DECL_ARMMODE(x) " .align 4\n" " .global " x "\n" " .arm\n" x ":\n"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
asm(
|
||||||
|
DECL_ARMMODE("convert_s16_float_asm")
|
||||||
|
DECL_ARMMODE("_convert_s16_float_asm")
|
||||||
|
"# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)\n"
|
||||||
|
" # Hacky way to get a constant of 2^-15.\n"
|
||||||
|
" # Might be faster to just load a constant from memory.\n"
|
||||||
|
" # It's just done once however ...\n"
|
||||||
|
" vmov.f32 q8, #0.25\n"
|
||||||
|
" vmul.f32 q8, q8, q8\n"
|
||||||
|
" vmul.f32 q8, q8, q8\n"
|
||||||
|
" vmul.f32 q8, q8, q8\n"
|
||||||
|
" vadd.f32 q8, q8, q8\n"
|
||||||
|
"\n"
|
||||||
|
" # Apply gain\n"
|
||||||
|
" vld1.f32 {d6[0]}, [r3]\n"
|
||||||
|
" vmul.f32 q8, q8, d6[0]\n"
|
||||||
|
"\n"
|
||||||
|
"1:\n"
|
||||||
|
" # Preload here?\n"
|
||||||
|
" vld1.s16 {q0}, [r1]!\n"
|
||||||
|
"\n"
|
||||||
|
" # Widen to 32-bit\n"
|
||||||
|
" vmovl.s16 q1, d0\n"
|
||||||
|
" vmovl.s16 q2, d1\n"
|
||||||
|
"\n"
|
||||||
|
" # Convert to float\n"
|
||||||
|
" vcvt.f32.s32 q1, q1\n"
|
||||||
|
" vcvt.f32.s32 q2, q2\n"
|
||||||
|
"\n"
|
||||||
|
" vmul.f32 q1, q1, q8\n"
|
||||||
|
" vmul.f32 q2, q2, q8\n"
|
||||||
|
"\n"
|
||||||
|
" vst1.f32 {q1-q2}, [r0]!\n"
|
||||||
|
"\n"
|
||||||
|
" # Guaranteed to get samples in multiples of 8.\n"
|
||||||
|
" subs r2, r2, #8\n"
|
||||||
|
" bne 1b\n"
|
||||||
|
"\n"
|
||||||
|
" bx lr\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
#endif
|
|
@ -0,0 +1,325 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (dsp_filter.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
|
||||||
|
#include <compat/posix_string.h>
|
||||||
|
#include <dynamic/dylib.h>
|
||||||
|
|
||||||
|
#include <file/file_path.h>
|
||||||
|
#include <file/config_file_userdata.h>
|
||||||
|
#include <features/features_cpu.h>
|
||||||
|
#include <lists/string_list.h>
|
||||||
|
#include <string/stdstring.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
#include <audio/dsp_filter.h>
|
||||||
|
|
||||||
|
struct retro_dsp_plug
|
||||||
|
{
|
||||||
|
#ifdef HAVE_DYLIB
|
||||||
|
dylib_t lib;
|
||||||
|
#endif
|
||||||
|
const struct dspfilter_implementation *impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct retro_dsp_instance
|
||||||
|
{
|
||||||
|
const struct dspfilter_implementation *impl;
|
||||||
|
void *impl_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct retro_dsp_filter
|
||||||
|
{
|
||||||
|
config_file_t *conf;
|
||||||
|
|
||||||
|
struct retro_dsp_plug *plugs;
|
||||||
|
unsigned num_plugs;
|
||||||
|
|
||||||
|
struct retro_dsp_instance *instances;
|
||||||
|
unsigned num_instances;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation *find_implementation(
|
||||||
|
retro_dsp_filter_t *dsp, const char *ident)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < dsp->num_plugs; i++)
|
||||||
|
{
|
||||||
|
if (string_is_equal(dsp->plugs[i].impl->short_ident, ident))
|
||||||
|
return dsp->plugs[i].impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_config dspfilter_config = {
|
||||||
|
config_userdata_get_float,
|
||||||
|
config_userdata_get_int,
|
||||||
|
config_userdata_get_float_array,
|
||||||
|
config_userdata_get_int_array,
|
||||||
|
config_userdata_get_string,
|
||||||
|
config_userdata_free,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool create_filter_graph(retro_dsp_filter_t *dsp, float sample_rate)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct retro_dsp_instance *instances = NULL;
|
||||||
|
unsigned filters = 0;
|
||||||
|
|
||||||
|
if (!config_get_uint(dsp->conf, "filters", &filters))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
instances = (struct retro_dsp_instance*)calloc(filters, sizeof(*instances));
|
||||||
|
if (!instances)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dsp->instances = instances;
|
||||||
|
dsp->num_instances = filters;
|
||||||
|
|
||||||
|
for (i = 0; i < filters; i++)
|
||||||
|
{
|
||||||
|
struct config_file_userdata userdata;
|
||||||
|
struct dspfilter_info info;
|
||||||
|
char key[64];
|
||||||
|
char name[64];
|
||||||
|
|
||||||
|
key[0] = name[0] = '\0';
|
||||||
|
|
||||||
|
info.input_rate = sample_rate;
|
||||||
|
|
||||||
|
snprintf(key, sizeof(key), "filter%u", i);
|
||||||
|
|
||||||
|
if (!config_get_array(dsp->conf, key, name, sizeof(name)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dsp->instances[i].impl = find_implementation(dsp, name);
|
||||||
|
if (!dsp->instances[i].impl)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
userdata.conf = dsp->conf;
|
||||||
|
/* Index-specific configs take priority over ident-specific. */
|
||||||
|
userdata.prefix[0] = key;
|
||||||
|
userdata.prefix[1] = dsp->instances[i].impl->short_ident;
|
||||||
|
|
||||||
|
dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info,
|
||||||
|
&dspfilter_config, &userdata);
|
||||||
|
if (!dsp->instances[i].impl_data)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_FILTERS_BUILTIN)
|
||||||
|
extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||||
|
extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||||
|
extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||||
|
extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||||
|
extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||||
|
extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||||
|
extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||||
|
|
||||||
|
static const dspfilter_get_implementation_t dsp_plugs_builtin[] = {
|
||||||
|
panning_dspfilter_get_implementation,
|
||||||
|
iir_dspfilter_get_implementation,
|
||||||
|
echo_dspfilter_get_implementation,
|
||||||
|
phaser_dspfilter_get_implementation,
|
||||||
|
wahwah_dspfilter_get_implementation,
|
||||||
|
eq_dspfilter_get_implementation,
|
||||||
|
chorus_dspfilter_get_implementation,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
|
||||||
|
struct retro_dsp_plug *plugs = (struct retro_dsp_plug*)
|
||||||
|
calloc(ARRAY_SIZE(dsp_plugs_builtin), sizeof(*plugs));
|
||||||
|
|
||||||
|
if (!plugs)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dsp->plugs = plugs;
|
||||||
|
dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++)
|
||||||
|
{
|
||||||
|
dsp->plugs[i].impl = dsp_plugs_builtin[i](mask);
|
||||||
|
if (!dsp->plugs[i].impl)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#elif defined(HAVE_DYLIB)
|
||||||
|
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
dspfilter_simd_mask_t mask = cpu_features_get();
|
||||||
|
unsigned list_size = list ? list->size : 0;
|
||||||
|
|
||||||
|
for (i = 0; i < list_size; i++)
|
||||||
|
{
|
||||||
|
dspfilter_get_implementation_t cb;
|
||||||
|
const struct dspfilter_implementation *impl = NULL;
|
||||||
|
struct retro_dsp_plug *new_plugs = NULL;
|
||||||
|
dylib_t lib =
|
||||||
|
dylib_load(list->elems[i].data);
|
||||||
|
|
||||||
|
if (!lib)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation");
|
||||||
|
if (!cb)
|
||||||
|
{
|
||||||
|
dylib_close(lib);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl = cb(mask);
|
||||||
|
if (!impl)
|
||||||
|
{
|
||||||
|
dylib_close(lib);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl->api_version != DSPFILTER_API_VERSION)
|
||||||
|
{
|
||||||
|
dylib_close(lib);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_plugs = (struct retro_dsp_plug*)
|
||||||
|
realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1));
|
||||||
|
if (!new_plugs)
|
||||||
|
{
|
||||||
|
dylib_close(lib);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Found plug. */
|
||||||
|
|
||||||
|
dsp->plugs = new_plugs;
|
||||||
|
dsp->plugs[dsp->num_plugs].lib = lib;
|
||||||
|
dsp->plugs[dsp->num_plugs].impl = impl;
|
||||||
|
dsp->num_plugs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
retro_dsp_filter_t *retro_dsp_filter_new(
|
||||||
|
const char *filter_config,
|
||||||
|
void *string_data,
|
||||||
|
float sample_rate)
|
||||||
|
{
|
||||||
|
config_file_t *conf = NULL;
|
||||||
|
struct string_list *plugs = NULL;
|
||||||
|
retro_dsp_filter_t *dsp = (retro_dsp_filter_t*)calloc(1, sizeof(*dsp));
|
||||||
|
|
||||||
|
if (!dsp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
conf = config_file_new(filter_config);
|
||||||
|
if (!conf) /* Did not find config. */
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
dsp->conf = conf;
|
||||||
|
|
||||||
|
if (string_data)
|
||||||
|
plugs = (struct string_list*)string_data;
|
||||||
|
|
||||||
|
#if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN)
|
||||||
|
if (!append_plugs(dsp, plugs))
|
||||||
|
goto error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (plugs)
|
||||||
|
string_list_free(plugs);
|
||||||
|
plugs = NULL;
|
||||||
|
|
||||||
|
if (!create_filter_graph(dsp, sample_rate))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return dsp;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (plugs)
|
||||||
|
string_list_free(plugs);
|
||||||
|
retro_dsp_filter_free(dsp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void retro_dsp_filter_free(retro_dsp_filter_t *dsp)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
if (!dsp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < dsp->num_instances; i++)
|
||||||
|
{
|
||||||
|
if (dsp->instances[i].impl_data && dsp->instances[i].impl)
|
||||||
|
dsp->instances[i].impl->free(dsp->instances[i].impl_data);
|
||||||
|
}
|
||||||
|
free(dsp->instances);
|
||||||
|
|
||||||
|
#ifdef HAVE_DYLIB
|
||||||
|
for (i = 0; i < dsp->num_plugs; i++)
|
||||||
|
{
|
||||||
|
if (dsp->plugs[i].lib)
|
||||||
|
dylib_close(dsp->plugs[i].lib);
|
||||||
|
}
|
||||||
|
free(dsp->plugs);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dsp->conf)
|
||||||
|
config_file_free(dsp->conf);
|
||||||
|
|
||||||
|
free(dsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void retro_dsp_filter_process(retro_dsp_filter_t *dsp,
|
||||||
|
struct retro_dsp_data *data)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct dspfilter_output output = {0};
|
||||||
|
struct dspfilter_input input = {0};
|
||||||
|
|
||||||
|
output.samples = data->input;
|
||||||
|
output.frames = data->input_frames;
|
||||||
|
|
||||||
|
for (i = 0; i < dsp->num_instances; i++)
|
||||||
|
{
|
||||||
|
input.samples = output.samples;
|
||||||
|
input.frames = output.frames;
|
||||||
|
dsp->instances[i].impl->process(
|
||||||
|
dsp->instances[i].impl_data, &output, &input);
|
||||||
|
}
|
||||||
|
|
||||||
|
data->output = output.samples;
|
||||||
|
data->output_frames = output.frames;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
filters = 2
|
||||||
|
filter0 = iir
|
||||||
|
filter1 = panning
|
||||||
|
|
||||||
|
iir_gain = 10.0
|
||||||
|
iir_type = BBOOST
|
||||||
|
iir_frequency = 200.0
|
||||||
|
|
||||||
|
# Avoids clipping.
|
||||||
|
panning_left_mix = "0.3 0.0"
|
||||||
|
panning_right_mix = "0.0 0.3"
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
filters = 4
|
||||||
|
filter0 = eq
|
||||||
|
filter1 = reverb
|
||||||
|
filter2 = iir
|
||||||
|
filter3 = panning
|
||||||
|
|
||||||
|
eq_frequencies = "32 64 125 250 500 1000 2000 4000 8000 16000 20000"
|
||||||
|
eq_gains = "6 9 12 7 6 5 7 9 11 6 0"
|
||||||
|
|
||||||
|
# Reverb - slight reverb
|
||||||
|
reverb_drytime = 0.5
|
||||||
|
reverb_wettime = 0.15
|
||||||
|
reverb_damping = 0.8
|
||||||
|
reverb_roomwidth = 0.25
|
||||||
|
reverb_roomsize = 0.25
|
||||||
|
|
||||||
|
# IIR - filters out some harsh sounds on the upper end
|
||||||
|
iir_type = RIAA_CD
|
||||||
|
|
||||||
|
# Panning - cut the volume a bit
|
||||||
|
panning_left_mix = "0.75 0.0"
|
||||||
|
panning_right_mix = "0.0 0.75"
|
|
@ -0,0 +1,14 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = chorus
|
||||||
|
|
||||||
|
# Controls the base delay of the chorus (milliseconds).
|
||||||
|
# chorus_delay_ms = 25.0
|
||||||
|
#
|
||||||
|
# Controls the depth of the delay. The delay will vary between delay_ms +/- depth_ms.
|
||||||
|
# chorus_depth_ms = 1.0
|
||||||
|
#
|
||||||
|
# Frequency of LFO which controls delay.
|
||||||
|
# chorus_lfo_freq = 0.5
|
||||||
|
#
|
||||||
|
# Controls dry/wet-ness of effect. 1.0 = full chorus, 0.0 = no chorus.
|
||||||
|
# chorus_drywet = 0.8
|
|
@ -0,0 +1,4 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = crystalizer
|
||||||
|
# Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max.
|
||||||
|
crystalizer_intensity = 5.0
|
|
@ -0,0 +1,41 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = eq
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
|
||||||
|
# Beta factor for Kaiser window.
|
||||||
|
# Lower values will allow better frequency resolution, but more ripple.
|
||||||
|
# eq_window_beta = 4.0
|
||||||
|
|
||||||
|
# The block size on which FFT is done.
|
||||||
|
# Too high value requires more processing as well as longer latency but
|
||||||
|
# allows finer-grained control over the spectrum.
|
||||||
|
# eq_block_size_log2 = 8
|
||||||
|
|
||||||
|
# An array of which frequencies to control.
|
||||||
|
# You can create an arbitrary amount of these sampling points.
|
||||||
|
# The EQ will try to create a frequency response which fits well to these points.
|
||||||
|
# The filter response is linearly interpolated between sampling points here.
|
||||||
|
#
|
||||||
|
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
|
||||||
|
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
|
||||||
|
#
|
||||||
|
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
|
||||||
|
# eq_frequencies = "500 1000 2000"
|
||||||
|
# eq_gains = "0 3 0"
|
||||||
|
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
|
||||||
|
|
||||||
|
# By default, this filter has a flat frequency response.
|
||||||
|
|
||||||
|
# Dumps the impulse response generated by the EQ as a plain-text file
|
||||||
|
# with one coefficient per line.
|
||||||
|
# eq_impulse_response_output = "eq_impulse.txt"
|
||||||
|
#
|
||||||
|
# Using GNU Octave or Matlab, you can plot the response with:
|
||||||
|
#
|
||||||
|
# f = fopen('/path/to/eq_impulse.txt');
|
||||||
|
# l = textscan(f, '%f');
|
||||||
|
# res = l{1};
|
||||||
|
# freqz(res, 1, 4096, 48000);
|
||||||
|
#
|
||||||
|
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch
|
|
@ -0,0 +1,19 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = echo
|
||||||
|
|
||||||
|
# Somewhat fancy Echo filter. Can take any number of echo channels with varying delays (ms) and feedback factors.
|
||||||
|
# Echo output from all channels can be fed back into each other to create a somewhat reverb-like effect if desired.
|
||||||
|
|
||||||
|
# Defaults, 200 ms delay echo with feedback:
|
||||||
|
# Delay in ms. Takes an array with multiple channels.
|
||||||
|
# echo_delay = "200"
|
||||||
|
# Feedback factor for echo.
|
||||||
|
# echo_feedback = "0.5"
|
||||||
|
# Overall echo amplification. If too high, the echo becomes unstable due to feedback.
|
||||||
|
# echo_amp = "0.2"
|
||||||
|
|
||||||
|
# Reverby preset.
|
||||||
|
# echo_delay = " 60 80 120 172 200 320 380"
|
||||||
|
# echo_feedback = "0.5 0.5 0.4 0.3 0.5 0.3 0.2"
|
||||||
|
|
||||||
|
# echo_amp = "0.12"
|
|
@ -0,0 +1,12 @@
|
||||||
|
filters = 2
|
||||||
|
filter0 = echo
|
||||||
|
filter1 = reverb
|
||||||
|
|
||||||
|
echo_delay = "200"
|
||||||
|
echo_feedback = "0.6"
|
||||||
|
echo_amp = "0.25"
|
||||||
|
|
||||||
|
reverb_roomwidth = 0.75
|
||||||
|
reverb_roomsize = 0.75
|
||||||
|
reverb_damping = 1.0
|
||||||
|
reverb_wettime = 0.3
|
|
@ -0,0 +1,7 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = iir
|
||||||
|
|
||||||
|
iir_gain = -12.0
|
||||||
|
iir_type = HSH
|
||||||
|
iir_frequency = 8000.0
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = iir
|
||||||
|
|
||||||
|
# Defaults.
|
||||||
|
#iir_frequency = 1024.0
|
||||||
|
#iir_quality = 0.707
|
||||||
|
#iir_gain = 0.0
|
||||||
|
#iir_type = LPF
|
||||||
|
|
||||||
|
# Filter types:
|
||||||
|
# LPF: Low-pass
|
||||||
|
# HPF: High-pass
|
||||||
|
# BPCSGF: Band-pass #1
|
||||||
|
# BPZPGF: Band-pass #2
|
||||||
|
# APF: Allpass
|
||||||
|
# NOTCH: Notch filter
|
||||||
|
# RIAA_phono: RIAA record/tape deemphasis
|
||||||
|
# PEQ: peaking band EQ
|
||||||
|
# BBOOST: Bassboost
|
||||||
|
# LSH: Low-shelf
|
||||||
|
# HSH: High-shelf
|
||||||
|
# RIAA_CD: CD de-emphasis
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = eq
|
||||||
|
|
||||||
|
eq_frequencies = "8000 10000 12500 16000 20000"
|
||||||
|
eq_gains = "0 -30 -30 -30 -30"
|
||||||
|
|
||||||
|
# Low pass filter for the QSound chip from CPS-1/2.
|
||||||
|
# Some games have aliasing due low quality samples, so you can hear some annoying noisy near 11 kHz
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
|
||||||
|
# Beta factor for Kaiser window.
|
||||||
|
# Lower values will allow better frequency resolution, but more ripple.
|
||||||
|
# eq_window_beta = 4.0
|
||||||
|
|
||||||
|
# The block size on which FFT is done.
|
||||||
|
# Too high value requires more processing as well as longer latency but
|
||||||
|
# allows finer-grained control over the spectrum.
|
||||||
|
# eq_block_size_log2 = 8
|
||||||
|
|
||||||
|
# An array of which frequencies to control.
|
||||||
|
# You can create an arbitrary amount of these sampling points.
|
||||||
|
# The EQ will try to create a frequency response which fits well to these points.
|
||||||
|
# The filter response is linearly interpolated between sampling points here.
|
||||||
|
#
|
||||||
|
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
|
||||||
|
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
|
||||||
|
#
|
||||||
|
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
|
||||||
|
# eq_frequencies = "500 1000 2000"
|
||||||
|
# eq_gains = "0 3 0"
|
||||||
|
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
|
||||||
|
|
||||||
|
# By default, this filter has a low pass response with cuttof frequency at ~8600 Hz.
|
||||||
|
|
||||||
|
# Dumps the impulse response generated by the EQ as a plain-text file
|
||||||
|
# with one coefficient per line.
|
||||||
|
# eq_impulse_response_output = "eq_impulse.txt"
|
||||||
|
#
|
||||||
|
# Using GNU Octave or Matlab, you can plot the response with:
|
||||||
|
#
|
||||||
|
# f = fopen('/path/to/eq_impulse.txt');
|
||||||
|
# l = textscan(f, '%f');
|
||||||
|
# res = l{1};
|
||||||
|
# freqz(res, 1, 4096, 48000);
|
||||||
|
#
|
||||||
|
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch
|
|
@ -0,0 +1,100 @@
|
||||||
|
compiler := gcc
|
||||||
|
extra_flags :=
|
||||||
|
use_neon := 0
|
||||||
|
build = release
|
||||||
|
DYLIB := so
|
||||||
|
PREFIX := /usr
|
||||||
|
INSTALLDIR := $(PREFIX)/lib/retroarch/filters/audio
|
||||||
|
|
||||||
|
ifeq ($(platform),)
|
||||||
|
platform = unix
|
||||||
|
ifeq ($(shell uname -a),)
|
||||||
|
platform = win
|
||||||
|
else ifneq ($(findstring MINGW,$(shell uname -a)),)
|
||||||
|
platform = win
|
||||||
|
else ifneq ($(findstring Darwin,$(shell uname -a)),)
|
||||||
|
platform = osx
|
||||||
|
arch = intel
|
||||||
|
ifeq ($(shell uname -p),powerpc)
|
||||||
|
arch = ppc
|
||||||
|
endif
|
||||||
|
else ifneq ($(findstring win,$(shell uname -a)),)
|
||||||
|
platform = win
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(platform),gcc)
|
||||||
|
extra_rules_gcc := $(shell $(compiler) -dumpmachine)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(findstring armv7,$(extra_rules_gcc)))
|
||||||
|
extra_flags += -mcpu=cortex-a9 -mtune=cortex-a9 -mfpu=neon
|
||||||
|
use_neon := 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(findstring hardfloat,$(extra_rules_gcc)))
|
||||||
|
extra_flags += -mfloat-abi=hard
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (release,$(build))
|
||||||
|
extra_flags += -O2
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (debug,$(build))
|
||||||
|
extra_flags += -O0 -g
|
||||||
|
endif
|
||||||
|
|
||||||
|
ldflags := $(LDFLAGS) -shared -lm -Wl,--version-script=link.T
|
||||||
|
|
||||||
|
ifeq ($(platform), unix)
|
||||||
|
DYLIB = so
|
||||||
|
else ifeq ($(platform), osx)
|
||||||
|
compiler := $(CC)
|
||||||
|
DYLIB = dylib
|
||||||
|
ldflags := -dynamiclib
|
||||||
|
else
|
||||||
|
extra_flags += -static-libgcc -static-libstdc++
|
||||||
|
DYLIB = dll
|
||||||
|
endif
|
||||||
|
|
||||||
|
CC := $(compiler) -Wall
|
||||||
|
CXX := $(subst CC,++,$(compiler)) -std=gnu++0x -Wall
|
||||||
|
flags := $(CPPFLAGS) $(CFLAGS) -fPIC $(extra_flags) -I../../include
|
||||||
|
asflags := $(ASFLAGS) -fPIC $(extra_flags)
|
||||||
|
objects :=
|
||||||
|
|
||||||
|
ifeq (1,$(use_neon))
|
||||||
|
ASMFLAGS := -INEON/asm
|
||||||
|
asflags += -mfpu=neon
|
||||||
|
endif
|
||||||
|
|
||||||
|
plugs := $(wildcard *.c)
|
||||||
|
objects := $(plugs:.c=.o)
|
||||||
|
targets := $(objects:.o=.$(DYLIB))
|
||||||
|
|
||||||
|
all: build;
|
||||||
|
|
||||||
|
%.o: %.S
|
||||||
|
$(CC) -c -o $@ $(asflags) $(ASMFLAGS) $<
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c -o $@ $(flags) $<
|
||||||
|
|
||||||
|
%.$(DYLIB): %.o
|
||||||
|
$(CC) -o $@ $(ldflags) $(flags) $^
|
||||||
|
|
||||||
|
build: $(targets)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
||||||
|
rm -f *.$(DYLIB)
|
||||||
|
|
||||||
|
strip:
|
||||||
|
strip -s *.$(DYLIB)
|
||||||
|
|
||||||
|
install:
|
||||||
|
mkdir -p $(DESTDIR)$(INSTALLDIR)
|
||||||
|
cp -t $(DESTDIR)$(INSTALLDIR) $(targets) *.dsp
|
||||||
|
|
||||||
|
test-install:
|
||||||
|
DESTDIR=/tmp/build $(MAKE) install
|
|
@ -0,0 +1,12 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = panning
|
||||||
|
|
||||||
|
# Gains are linear.
|
||||||
|
|
||||||
|
# Stereo Mono:
|
||||||
|
panning_left_mix = "0.5 0.5"
|
||||||
|
panning_right_mix = "0.5 0.5"
|
||||||
|
|
||||||
|
# Mono on one speaker:
|
||||||
|
# panning_left_mix = "0.5 0.5"
|
||||||
|
# panning_right_mix = "0.0 0.0"
|
|
@ -0,0 +1,23 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = panning
|
||||||
|
|
||||||
|
# Gains are linear.
|
||||||
|
|
||||||
|
# The default. Left and right channels map to each other.
|
||||||
|
panning_left_mix = "1.0 0.0"
|
||||||
|
panning_right_mix = "0.0 1.0"
|
||||||
|
|
||||||
|
# Some examples:
|
||||||
|
#
|
||||||
|
# Mono:
|
||||||
|
# panning_left_mix = "0.5 0.5"
|
||||||
|
# panning_right_mix = "0.5 0.5"
|
||||||
|
|
||||||
|
# Swap left and right channels:
|
||||||
|
# panning_left_mix = "0.0 1.0"
|
||||||
|
# panning_right_mix = "1.0 0.0"
|
||||||
|
#
|
||||||
|
# Mono on one speaker:
|
||||||
|
# panning_left_mix = "0.5 0.5"
|
||||||
|
# panning_right_mix = "0.0 0.0"
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = phaser
|
||||||
|
|
||||||
|
# Defaults.
|
||||||
|
# phaser_lfo_freq = 0.4
|
||||||
|
# phaser_lfo_start_phase = 0.0
|
||||||
|
# phaser_feedback = 0.0
|
||||||
|
# phaser_depth = 0.4
|
||||||
|
# phaser_dry_wet = 0.5
|
||||||
|
# phaser_stages = 2
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = reverb
|
||||||
|
|
||||||
|
# Defaults.
|
||||||
|
# reverb_drytime = 0.43
|
||||||
|
# reverb_wettime = 0.4
|
||||||
|
# reverb_damping = 0.8
|
||||||
|
# reverb_roomwidth = 0.56
|
||||||
|
# reverb_roomsize = 0.56
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = wahwah
|
||||||
|
|
||||||
|
# Defaults.
|
||||||
|
# wahwah_lfo_freq = 1.5
|
||||||
|
# wahwah_lfo_start_phase = 0.0
|
||||||
|
# wahwah_freq_offset = 0.3
|
||||||
|
# wahwah_depth = 0.7
|
||||||
|
# wahwah_resonance = 2.5
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (chorus.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
#define CHORUS_MAX_DELAY 4096
|
||||||
|
#define CHORUS_DELAY_MASK (CHORUS_MAX_DELAY - 1)
|
||||||
|
|
||||||
|
struct chorus_data
|
||||||
|
{
|
||||||
|
float old[2][CHORUS_MAX_DELAY];
|
||||||
|
unsigned old_ptr;
|
||||||
|
|
||||||
|
float delay;
|
||||||
|
float depth;
|
||||||
|
float input_rate;
|
||||||
|
float mix_dry;
|
||||||
|
float mix_wet;
|
||||||
|
unsigned lfo_ptr;
|
||||||
|
unsigned lfo_period;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void chorus_free(void *data)
|
||||||
|
{
|
||||||
|
if (data)
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void chorus_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
float *out = NULL;
|
||||||
|
struct chorus_data *ch = (struct chorus_data*)data;
|
||||||
|
|
||||||
|
output->samples = input->samples;
|
||||||
|
output->frames = input->frames;
|
||||||
|
out = output->samples;
|
||||||
|
|
||||||
|
for (i = 0; i < input->frames; i++, out += 2)
|
||||||
|
{
|
||||||
|
unsigned delay_int;
|
||||||
|
float delay_frac, l_a, l_b, r_a, r_b;
|
||||||
|
float chorus_l, chorus_r;
|
||||||
|
float in[2] = { out[0], out[1] };
|
||||||
|
float delay = ch->delay + ch->depth * sin((2.0 * M_PI * ch->lfo_ptr++) / ch->lfo_period);
|
||||||
|
|
||||||
|
delay *= ch->input_rate;
|
||||||
|
if (ch->lfo_ptr >= ch->lfo_period)
|
||||||
|
ch->lfo_ptr = 0;
|
||||||
|
|
||||||
|
delay_int = (unsigned)delay;
|
||||||
|
|
||||||
|
if (delay_int >= CHORUS_MAX_DELAY - 1)
|
||||||
|
delay_int = CHORUS_MAX_DELAY - 2;
|
||||||
|
|
||||||
|
delay_frac = delay - delay_int;
|
||||||
|
|
||||||
|
ch->old[0][ch->old_ptr] = in[0];
|
||||||
|
ch->old[1][ch->old_ptr] = in[1];
|
||||||
|
|
||||||
|
l_a = ch->old[0][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
|
||||||
|
l_b = ch->old[0][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];
|
||||||
|
r_a = ch->old[1][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
|
||||||
|
r_b = ch->old[1][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];
|
||||||
|
|
||||||
|
/* Lerp introduces aliasing of the chorus component,
|
||||||
|
* but doing full polyphase here is probably overkill. */
|
||||||
|
chorus_l = l_a * (1.0f - delay_frac) + l_b * delay_frac;
|
||||||
|
chorus_r = r_a * (1.0f - delay_frac) + r_b * delay_frac;
|
||||||
|
|
||||||
|
out[0] = ch->mix_dry * in[0] + ch->mix_wet * chorus_l;
|
||||||
|
out[1] = ch->mix_dry * in[1] + ch->mix_wet * chorus_r;
|
||||||
|
|
||||||
|
ch->old_ptr = (ch->old_ptr + 1) & CHORUS_DELAY_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *chorus_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
float delay, depth, lfo_freq, drywet;
|
||||||
|
struct chorus_data *ch = (struct chorus_data*)calloc(1, sizeof(*ch));
|
||||||
|
if (!ch)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
config->get_float(userdata, "delay_ms", &delay, 25.0f);
|
||||||
|
config->get_float(userdata, "depth_ms", &depth, 1.0f);
|
||||||
|
config->get_float(userdata, "lfo_freq", &lfo_freq, 0.5f);
|
||||||
|
config->get_float(userdata, "drywet", &drywet, 0.8f);
|
||||||
|
|
||||||
|
delay /= 1000.0f;
|
||||||
|
depth /= 1000.0f;
|
||||||
|
|
||||||
|
if (depth > delay)
|
||||||
|
depth = delay;
|
||||||
|
|
||||||
|
if (drywet < 0.0f)
|
||||||
|
drywet = 0.0f;
|
||||||
|
else if (drywet > 1.0f)
|
||||||
|
drywet = 1.0f;
|
||||||
|
|
||||||
|
ch->mix_dry = 1.0f - 0.5f * drywet;
|
||||||
|
ch->mix_wet = 0.5f * drywet;
|
||||||
|
|
||||||
|
ch->delay = delay;
|
||||||
|
ch->depth = depth;
|
||||||
|
ch->lfo_period = (1.0f / lfo_freq) * info->input_rate;
|
||||||
|
ch->input_rate = info->input_rate;
|
||||||
|
if (!ch->lfo_period)
|
||||||
|
ch->lfo_period = 1;
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation chorus_plug = {
|
||||||
|
chorus_init,
|
||||||
|
chorus_process,
|
||||||
|
chorus_free,
|
||||||
|
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"Chorus",
|
||||||
|
"chorus",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation chorus_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *
|
||||||
|
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &chorus_plug;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (echo.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
struct delta_data
|
||||||
|
{
|
||||||
|
float intensity;
|
||||||
|
float old[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void delta_free(void *data)
|
||||||
|
{
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void delta_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
unsigned i, c;
|
||||||
|
struct delta_data *d = (struct delta_data*)data;
|
||||||
|
float *out = output->samples;
|
||||||
|
output->samples = input->samples;
|
||||||
|
output->frames = input->frames;
|
||||||
|
|
||||||
|
for (i = 0; i < input->frames; i++)
|
||||||
|
{
|
||||||
|
for (c = 0; c < 2; c++)
|
||||||
|
{
|
||||||
|
float current = *out;
|
||||||
|
*out++ = current + (current - d->old[c]) * d->intensity;
|
||||||
|
d->old[c] = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *delta_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
struct delta_data *d = (struct delta_data*)calloc(1, sizeof(*d));
|
||||||
|
if (!d)
|
||||||
|
return NULL;
|
||||||
|
config->get_float(userdata, "intensity", &d->intensity, 5.0f);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation delta_plug = {
|
||||||
|
delta_init,
|
||||||
|
delta_process,
|
||||||
|
delta_free,
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"Delta Sharpening",
|
||||||
|
"crystalizer",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation delta_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &delta_plug;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (echo.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
struct echo_channel
|
||||||
|
{
|
||||||
|
float *buffer;
|
||||||
|
unsigned ptr;
|
||||||
|
unsigned frames;
|
||||||
|
float feedback;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct echo_data
|
||||||
|
{
|
||||||
|
struct echo_channel *channels;
|
||||||
|
unsigned num_channels;
|
||||||
|
float amp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void echo_free(void *data)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct echo_data *echo = (struct echo_data*)data;
|
||||||
|
|
||||||
|
for (i = 0; i < echo->num_channels; i++)
|
||||||
|
free(echo->channels[i].buffer);
|
||||||
|
free(echo->channels);
|
||||||
|
free(echo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void echo_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
unsigned i, c;
|
||||||
|
float *out = NULL;
|
||||||
|
struct echo_data *echo = (struct echo_data*)data;
|
||||||
|
|
||||||
|
output->samples = input->samples;
|
||||||
|
output->frames = input->frames;
|
||||||
|
|
||||||
|
out = output->samples;
|
||||||
|
|
||||||
|
for (i = 0; i < input->frames; i++, out += 2)
|
||||||
|
{
|
||||||
|
float left, right;
|
||||||
|
float echo_left = 0.0f;
|
||||||
|
float echo_right = 0.0f;
|
||||||
|
|
||||||
|
for (c = 0; c < echo->num_channels; c++)
|
||||||
|
{
|
||||||
|
echo_left += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0];
|
||||||
|
echo_right += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo_left *= echo->amp;
|
||||||
|
echo_right *= echo->amp;
|
||||||
|
|
||||||
|
left = out[0] + echo_left;
|
||||||
|
right = out[1] + echo_right;
|
||||||
|
|
||||||
|
for (c = 0; c < echo->num_channels; c++)
|
||||||
|
{
|
||||||
|
float feedback_left = out[0] + echo->channels[c].feedback * echo_left;
|
||||||
|
float feedback_right = out[1] + echo->channels[c].feedback * echo_right;
|
||||||
|
|
||||||
|
echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0] = feedback_left;
|
||||||
|
echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1] = feedback_right;
|
||||||
|
|
||||||
|
echo->channels[c].ptr = (echo->channels[c].ptr + 1) % echo->channels[c].frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
out[0] = left;
|
||||||
|
out[1] = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *echo_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
unsigned i, channels;
|
||||||
|
struct echo_channel *echo_channels = NULL;
|
||||||
|
float *delay = NULL;
|
||||||
|
float *feedback = NULL;
|
||||||
|
unsigned num_delay = 0;
|
||||||
|
unsigned num_feedback = 0;
|
||||||
|
|
||||||
|
static const float default_delay[] = { 200.0f };
|
||||||
|
static const float default_feedback[] = { 0.5f };
|
||||||
|
struct echo_data *echo = (struct echo_data*)
|
||||||
|
calloc(1, sizeof(*echo));
|
||||||
|
|
||||||
|
if (!echo)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
config->get_float_array(userdata, "delay", &delay,
|
||||||
|
&num_delay, default_delay, 1);
|
||||||
|
config->get_float_array(userdata, "feedback", &feedback,
|
||||||
|
&num_feedback, default_feedback, 1);
|
||||||
|
config->get_float(userdata, "amp", &echo->amp, 0.2f);
|
||||||
|
|
||||||
|
channels = num_feedback = num_delay = MIN(num_delay, num_feedback);
|
||||||
|
|
||||||
|
echo_channels = (struct echo_channel*)calloc(channels,
|
||||||
|
sizeof(*echo_channels));
|
||||||
|
|
||||||
|
if (!echo_channels)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
echo->channels = echo_channels;
|
||||||
|
echo->num_channels = channels;
|
||||||
|
|
||||||
|
for (i = 0; i < channels; i++)
|
||||||
|
{
|
||||||
|
unsigned frames = (unsigned)(delay[i] * info->input_rate / 1000.0f + 0.5f);
|
||||||
|
if (!frames)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
echo->channels[i].buffer = (float*)calloc(frames, 2 * sizeof(float));
|
||||||
|
if (!echo->channels[i].buffer)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
echo->channels[i].frames = frames;
|
||||||
|
echo->channels[i].feedback = feedback[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
config->free(delay);
|
||||||
|
config->free(feedback);
|
||||||
|
return echo;
|
||||||
|
|
||||||
|
error:
|
||||||
|
config->free(delay);
|
||||||
|
config->free(feedback);
|
||||||
|
echo_free(echo);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation echo_plug = {
|
||||||
|
echo_init,
|
||||||
|
echo_process,
|
||||||
|
echo_free,
|
||||||
|
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"Multi-Echo",
|
||||||
|
"echo",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation echo_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &echo_plug;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
|
@ -0,0 +1,352 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (eq.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <retro_inline.h>
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <filters.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
#include "fft/fft.c"
|
||||||
|
|
||||||
|
struct eq_data
|
||||||
|
{
|
||||||
|
fft_t *fft;
|
||||||
|
float buffer[8 * 1024];
|
||||||
|
|
||||||
|
float *save;
|
||||||
|
float *block;
|
||||||
|
fft_complex_t *filter;
|
||||||
|
fft_complex_t *fftblock;
|
||||||
|
unsigned block_size;
|
||||||
|
unsigned block_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct eq_gain
|
||||||
|
{
|
||||||
|
float freq;
|
||||||
|
float gain; /* Linear. */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void eq_free(void *data)
|
||||||
|
{
|
||||||
|
struct eq_data *eq = (struct eq_data*)data;
|
||||||
|
if (!eq)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fft_free(eq->fft);
|
||||||
|
free(eq->save);
|
||||||
|
free(eq->block);
|
||||||
|
free(eq->fftblock);
|
||||||
|
free(eq->filter);
|
||||||
|
free(eq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void eq_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
float *out;
|
||||||
|
const float *in;
|
||||||
|
unsigned input_frames;
|
||||||
|
struct eq_data *eq = (struct eq_data*)data;
|
||||||
|
|
||||||
|
output->samples = eq->buffer;
|
||||||
|
output->frames = 0;
|
||||||
|
|
||||||
|
out = eq->buffer;
|
||||||
|
in = input->samples;
|
||||||
|
input_frames = input->frames;
|
||||||
|
|
||||||
|
while (input_frames)
|
||||||
|
{
|
||||||
|
unsigned write_avail = eq->block_size - eq->block_ptr;
|
||||||
|
|
||||||
|
if (input_frames < write_avail)
|
||||||
|
write_avail = input_frames;
|
||||||
|
|
||||||
|
memcpy(eq->block + eq->block_ptr * 2, in, write_avail * 2 * sizeof(float));
|
||||||
|
|
||||||
|
in += write_avail * 2;
|
||||||
|
input_frames -= write_avail;
|
||||||
|
eq->block_ptr += write_avail;
|
||||||
|
|
||||||
|
// Convolve a new block.
|
||||||
|
if (eq->block_ptr == eq->block_size)
|
||||||
|
{
|
||||||
|
unsigned i, c;
|
||||||
|
|
||||||
|
for (c = 0; c < 2; c++)
|
||||||
|
{
|
||||||
|
fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2);
|
||||||
|
for (i = 0; i < 2 * eq->block_size; i++)
|
||||||
|
eq->fftblock[i] = fft_complex_mul(eq->fftblock[i], eq->filter[i]);
|
||||||
|
fft_process_inverse(eq->fft, out + c, eq->fftblock, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overlap add method, so add in saved block now.
|
||||||
|
for (i = 0; i < 2 * eq->block_size; i++)
|
||||||
|
out[i] += eq->save[i];
|
||||||
|
|
||||||
|
// Save block for later.
|
||||||
|
memcpy(eq->save, out + 2 * eq->block_size, 2 * eq->block_size * sizeof(float));
|
||||||
|
|
||||||
|
out += eq->block_size * 2;
|
||||||
|
output->frames += eq->block_size;
|
||||||
|
eq->block_ptr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gains_cmp(const void *a_, const void *b_)
|
||||||
|
{
|
||||||
|
const struct eq_gain *a = (const struct eq_gain*)a_;
|
||||||
|
const struct eq_gain *b = (const struct eq_gain*)b_;
|
||||||
|
if (a->freq < b->freq)
|
||||||
|
return -1;
|
||||||
|
if (a->freq > b->freq)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generate_response(fft_complex_t *response,
|
||||||
|
const struct eq_gain *gains, unsigned num_gains, unsigned samples)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
float start_freq = 0.0f;
|
||||||
|
float start_gain = 1.0f;
|
||||||
|
|
||||||
|
float end_freq = 1.0f;
|
||||||
|
float end_gain = 1.0f;
|
||||||
|
|
||||||
|
if (num_gains)
|
||||||
|
{
|
||||||
|
end_freq = gains->freq;
|
||||||
|
end_gain = gains->gain;
|
||||||
|
num_gains--;
|
||||||
|
gains++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a response by linear interpolation between
|
||||||
|
* known frequency sample points. */
|
||||||
|
for (i = 0; i <= samples; i++)
|
||||||
|
{
|
||||||
|
float gain;
|
||||||
|
float lerp = 0.5f;
|
||||||
|
float freq = (float)i / samples;
|
||||||
|
|
||||||
|
while (freq >= end_freq)
|
||||||
|
{
|
||||||
|
if (num_gains)
|
||||||
|
{
|
||||||
|
start_freq = end_freq;
|
||||||
|
start_gain = end_gain;
|
||||||
|
end_freq = gains->freq;
|
||||||
|
end_gain = gains->gain;
|
||||||
|
|
||||||
|
gains++;
|
||||||
|
num_gains--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
start_freq = end_freq;
|
||||||
|
start_gain = end_gain;
|
||||||
|
end_freq = 1.0f;
|
||||||
|
end_gain = 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Edge case where i == samples. */
|
||||||
|
if (end_freq > start_freq)
|
||||||
|
lerp = (freq - start_freq) / (end_freq - start_freq);
|
||||||
|
gain = (1.0f - lerp) * start_gain + lerp * end_gain;
|
||||||
|
|
||||||
|
response[i].real = gain;
|
||||||
|
response[i].imag = 0.0f;
|
||||||
|
response[2 * samples - i].real = gain;
|
||||||
|
response[2 * samples - i].imag = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_filter(struct eq_data *eq, unsigned size_log2,
|
||||||
|
struct eq_gain *gains, unsigned num_gains, double beta, const char *filter_path)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int half_block_size = eq->block_size >> 1;
|
||||||
|
double window_mod = 1.0 / kaiser_window_function(0.0, beta);
|
||||||
|
|
||||||
|
fft_t *fft = fft_new(size_log2);
|
||||||
|
float *time_filter = (float*)calloc(eq->block_size * 2 + 1, sizeof(*time_filter));
|
||||||
|
if (!fft || !time_filter)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* Make sure bands are in correct order. */
|
||||||
|
qsort(gains, num_gains, sizeof(*gains), gains_cmp);
|
||||||
|
|
||||||
|
/* Compute desired filter response. */
|
||||||
|
generate_response(eq->filter, gains, num_gains, half_block_size);
|
||||||
|
|
||||||
|
/* Get equivalent time-domain filter. */
|
||||||
|
fft_process_inverse(fft, time_filter, eq->filter, 1);
|
||||||
|
|
||||||
|
/* ifftshift() to create the correct linear phase filter.
|
||||||
|
* The filter response was designed with zero phase, which
|
||||||
|
* won't work unless we compensate
|
||||||
|
* for the repeating property of the FFT here
|
||||||
|
* by flipping left and right blocks. */
|
||||||
|
for (i = 0; i < half_block_size; i++)
|
||||||
|
{
|
||||||
|
float tmp = time_filter[i + half_block_size];
|
||||||
|
time_filter[i + half_block_size] = time_filter[i];
|
||||||
|
time_filter[i] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply a window to smooth out the frequency repsonse. */
|
||||||
|
for (i = 0; i < (int)eq->block_size; i++)
|
||||||
|
{
|
||||||
|
/* Kaiser window. */
|
||||||
|
double phase = (double)i / eq->block_size;
|
||||||
|
phase = 2.0 * (phase - 0.5);
|
||||||
|
time_filter[i] *= window_mod * kaiser_window_function(phase, beta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debugging. */
|
||||||
|
if (filter_path)
|
||||||
|
{
|
||||||
|
FILE *file = fopen(filter_path, "w");
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
for (i = 0; i < (int)eq->block_size - 1; i++)
|
||||||
|
fprintf(file, "%.8f\n", time_filter[i + 1]);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Padded FFT to create our FFT filter.
|
||||||
|
* Make our even-length filter odd by discarding the first coefficient.
|
||||||
|
* For some interesting reason, this allows us to design an odd-length linear phase filter.
|
||||||
|
*/
|
||||||
|
fft_process_forward(eq->fft, eq->filter, time_filter + 1, 1);
|
||||||
|
|
||||||
|
end:
|
||||||
|
fft_free(fft);
|
||||||
|
free(time_filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *eq_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
float *frequencies, *gain;
|
||||||
|
unsigned num_freq, num_gain, i, size;
|
||||||
|
int size_log2;
|
||||||
|
float beta;
|
||||||
|
struct eq_gain *gains = NULL;
|
||||||
|
char *filter_path = NULL;
|
||||||
|
const float default_freq[] = { 0.0f, info->input_rate };
|
||||||
|
const float default_gain[] = { 0.0f, 0.0f };
|
||||||
|
struct eq_data *eq = (struct eq_data*)calloc(1, sizeof(*eq));
|
||||||
|
if (!eq)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
|
||||||
|
config->get_float(userdata, "window_beta", &beta, 4.0f);
|
||||||
|
|
||||||
|
config->get_int(userdata, "block_size_log2", &size_log2, 8);
|
||||||
|
size = 1 << size_log2;
|
||||||
|
|
||||||
|
config->get_float_array(userdata, "frequencies", &frequencies, &num_freq, default_freq, 2);
|
||||||
|
config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2);
|
||||||
|
|
||||||
|
if (!config->get_string(userdata, "impulse_response_output", &filter_path, ""))
|
||||||
|
{
|
||||||
|
config->free(filter_path);
|
||||||
|
filter_path = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_gain = num_freq = MIN(num_gain, num_freq);
|
||||||
|
|
||||||
|
gains = (struct eq_gain*)calloc(num_gain, sizeof(*gains));
|
||||||
|
if (!gains)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
for (i = 0; i < num_gain; i++)
|
||||||
|
{
|
||||||
|
gains[i].freq = frequencies[i] / (0.5f * info->input_rate);
|
||||||
|
gains[i].gain = pow(10.0, gain[i] / 20.0);
|
||||||
|
}
|
||||||
|
config->free(frequencies);
|
||||||
|
config->free(gain);
|
||||||
|
|
||||||
|
eq->block_size = size;
|
||||||
|
|
||||||
|
eq->save = (float*)calloc( size, 2 * sizeof(*eq->save));
|
||||||
|
eq->block = (float*)calloc(2 * size, 2 * sizeof(*eq->block));
|
||||||
|
eq->fftblock = (fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock));
|
||||||
|
eq->filter = (fft_complex_t*)calloc(2 * size, sizeof(*eq->filter));
|
||||||
|
|
||||||
|
/* Use an FFT which is twice the block size with zero-padding
|
||||||
|
* to make circular convolution => proper convolution.
|
||||||
|
*/
|
||||||
|
eq->fft = fft_new(size_log2 + 1);
|
||||||
|
|
||||||
|
if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
create_filter(eq, size_log2, gains, num_gain, beta, filter_path);
|
||||||
|
config->free(filter_path);
|
||||||
|
filter_path = NULL;
|
||||||
|
|
||||||
|
free(gains);
|
||||||
|
return eq;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(gains);
|
||||||
|
eq_free(eq);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation eq_plug = {
|
||||||
|
eq_init,
|
||||||
|
eq_process,
|
||||||
|
eq_free,
|
||||||
|
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"Linear-Phase FFT Equalizer",
|
||||||
|
"eq",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation eq_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &eq_plug;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (fft.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "fft.h"
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
|
||||||
|
struct fft
|
||||||
|
{
|
||||||
|
fft_complex_t *interleave_buffer;
|
||||||
|
fft_complex_t *phase_lut;
|
||||||
|
unsigned *bitinverse_buffer;
|
||||||
|
unsigned size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned bitswap(unsigned x, unsigned size_log2)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
unsigned ret = 0;
|
||||||
|
for (i = 0; i < size_log2; i++)
|
||||||
|
ret |= ((x >> i) & 1) << (size_log2 - i - 1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_bitinverse(unsigned *bitinverse, unsigned size_log2)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
unsigned size = 1 << size_log2;
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
bitinverse[i] = bitswap(i, size_log2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fft_complex_t exp_imag(double phase)
|
||||||
|
{
|
||||||
|
fft_complex_t out = { cos(phase), sin(phase) };
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_phase_lut(fft_complex_t *out, int size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
out += size;
|
||||||
|
for (i = -size; i <= size; i++)
|
||||||
|
out[i] = exp_imag((M_PI * i) / size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void interleave_complex(const unsigned *bitinverse,
|
||||||
|
fft_complex_t *out, const fft_complex_t *in,
|
||||||
|
unsigned samples, unsigned step)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < samples; i++, in += step)
|
||||||
|
out[bitinverse[i]] = *in;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void interleave_float(const unsigned *bitinverse,
|
||||||
|
fft_complex_t *out, const float *in,
|
||||||
|
unsigned samples, unsigned step)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < samples; i++, in += step)
|
||||||
|
{
|
||||||
|
unsigned inv_i = bitinverse[i];
|
||||||
|
out[inv_i].real = *in;
|
||||||
|
out[inv_i].imag = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void resolve_float(float *out, const fft_complex_t *in, unsigned samples,
|
||||||
|
float gain, unsigned step)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < samples; i++, in++, out += step)
|
||||||
|
*out = gain * in->real;
|
||||||
|
}
|
||||||
|
|
||||||
|
fft_t *fft_new(unsigned block_size_log2)
|
||||||
|
{
|
||||||
|
unsigned size;
|
||||||
|
fft_t *fft = (fft_t*)calloc(1, sizeof(*fft));
|
||||||
|
if (!fft)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size = 1 << block_size_log2;
|
||||||
|
fft->interleave_buffer = (fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer));
|
||||||
|
fft->bitinverse_buffer = (unsigned*)calloc(size, sizeof(*fft->bitinverse_buffer));
|
||||||
|
fft->phase_lut = (fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut));
|
||||||
|
|
||||||
|
if (!fft->interleave_buffer || !fft->bitinverse_buffer || !fft->phase_lut)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
fft->size = size;
|
||||||
|
|
||||||
|
build_bitinverse(fft->bitinverse_buffer, block_size_log2);
|
||||||
|
build_phase_lut(fft->phase_lut, size);
|
||||||
|
return fft;
|
||||||
|
|
||||||
|
error:
|
||||||
|
fft_free(fft);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fft_free(fft_t *fft)
|
||||||
|
{
|
||||||
|
if (!fft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(fft->interleave_buffer);
|
||||||
|
free(fft->bitinverse_buffer);
|
||||||
|
free(fft->phase_lut);
|
||||||
|
free(fft);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void butterfly(fft_complex_t *a, fft_complex_t *b, fft_complex_t mod)
|
||||||
|
{
|
||||||
|
mod = fft_complex_mul(mod, *b);
|
||||||
|
*b = fft_complex_sub(*a, mod);
|
||||||
|
*a = fft_complex_add(*a, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void butterflies(fft_complex_t *butterfly_buf,
|
||||||
|
const fft_complex_t *phase_lut,
|
||||||
|
int phase_dir, unsigned step_size, unsigned samples)
|
||||||
|
{
|
||||||
|
unsigned i, j;
|
||||||
|
for (i = 0; i < samples; i += step_size << 1)
|
||||||
|
{
|
||||||
|
int phase_step = (int)samples * phase_dir / (int)step_size;
|
||||||
|
for (j = i; j < i + step_size; j++)
|
||||||
|
butterfly(&butterfly_buf[j], &butterfly_buf[j + step_size],
|
||||||
|
phase_lut[phase_step * (int)(j - i)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fft_process_forward_complex(fft_t *fft,
|
||||||
|
fft_complex_t *out, const fft_complex_t *in, unsigned step)
|
||||||
|
{
|
||||||
|
unsigned step_size;
|
||||||
|
unsigned samples = fft->size;
|
||||||
|
interleave_complex(fft->bitinverse_buffer, out, in, samples, step);
|
||||||
|
|
||||||
|
for (step_size = 1; step_size < samples; step_size <<= 1)
|
||||||
|
{
|
||||||
|
butterflies(out,
|
||||||
|
fft->phase_lut + samples,
|
||||||
|
-1, step_size, samples);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fft_process_forward(fft_t *fft,
|
||||||
|
fft_complex_t *out, const float *in, unsigned step)
|
||||||
|
{
|
||||||
|
unsigned step_size;
|
||||||
|
unsigned samples = fft->size;
|
||||||
|
interleave_float(fft->bitinverse_buffer, out, in, samples, step);
|
||||||
|
|
||||||
|
for (step_size = 1; step_size < fft->size; step_size <<= 1)
|
||||||
|
{
|
||||||
|
butterflies(out,
|
||||||
|
fft->phase_lut + samples,
|
||||||
|
-1, step_size, samples);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fft_process_inverse(fft_t *fft,
|
||||||
|
float *out, const fft_complex_t *in, unsigned step)
|
||||||
|
{
|
||||||
|
unsigned step_size;
|
||||||
|
unsigned samples = fft->size;
|
||||||
|
|
||||||
|
interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer,
|
||||||
|
in, samples, 1);
|
||||||
|
|
||||||
|
for (step_size = 1; step_size < samples; step_size <<= 1)
|
||||||
|
{
|
||||||
|
butterflies(fft->interleave_buffer,
|
||||||
|
fft->phase_lut + samples,
|
||||||
|
1, step_size, samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples, step);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (fft.h).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RARCH_FFT_H__
|
||||||
|
#define RARCH_FFT_H__
|
||||||
|
|
||||||
|
#include <retro_inline.h>
|
||||||
|
#include <math/complex.h>
|
||||||
|
|
||||||
|
typedef struct fft fft_t;
|
||||||
|
|
||||||
|
fft_t *fft_new(unsigned block_size_log2);
|
||||||
|
|
||||||
|
void fft_free(fft_t *fft);
|
||||||
|
|
||||||
|
void fft_process_forward_complex(fft_t *fft,
|
||||||
|
fft_complex_t *out, const fft_complex_t *in, unsigned step);
|
||||||
|
|
||||||
|
void fft_process_forward(fft_t *fft,
|
||||||
|
fft_complex_t *out, const float *in, unsigned step);
|
||||||
|
|
||||||
|
void fft_process_inverse(fft_t *fft,
|
||||||
|
float *out, const fft_complex_t *in, unsigned step);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,372 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (iir.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
#include <string/stdstring.h>
|
||||||
|
|
||||||
|
#define sqr(a) ((a) * (a))
|
||||||
|
|
||||||
|
/* filter types */
|
||||||
|
enum IIRFilter
|
||||||
|
{
|
||||||
|
LPF, /* low pass filter */
|
||||||
|
HPF, /* High pass filter */
|
||||||
|
BPCSGF, /* band pass filter 1 */
|
||||||
|
BPZPGF, /* band pass filter 2 */
|
||||||
|
APF, /* Allpass filter*/
|
||||||
|
NOTCH, /* Notch Filter */
|
||||||
|
RIAA_phono, /* RIAA record/tape deemphasis */
|
||||||
|
PEQ, /* Peaking band EQ filter */
|
||||||
|
BBOOST, /* Bassboost filter */
|
||||||
|
LSH, /* Low shelf filter */
|
||||||
|
HSH, /* High shelf filter */
|
||||||
|
RIAA_CD /* CD de-emphasis */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iir_data
|
||||||
|
{
|
||||||
|
float b0, b1, b2;
|
||||||
|
float a0, a1, a2;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
float xn1, xn2;
|
||||||
|
float yn1, yn2;
|
||||||
|
} l, r;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void iir_free(void *data)
|
||||||
|
{
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iir_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct iir_data *iir = (struct iir_data*)data;
|
||||||
|
float *out = output->samples;
|
||||||
|
|
||||||
|
float b0 = iir->b0;
|
||||||
|
float b1 = iir->b1;
|
||||||
|
float b2 = iir->b2;
|
||||||
|
float a0 = iir->a0;
|
||||||
|
float a1 = iir->a1;
|
||||||
|
float a2 = iir->a2;
|
||||||
|
|
||||||
|
float xn1_l = iir->l.xn1;
|
||||||
|
float xn2_l = iir->l.xn2;
|
||||||
|
float yn1_l = iir->l.yn1;
|
||||||
|
float yn2_l = iir->l.yn2;
|
||||||
|
|
||||||
|
float xn1_r = iir->r.xn1;
|
||||||
|
float xn2_r = iir->r.xn2;
|
||||||
|
float yn1_r = iir->r.yn1;
|
||||||
|
float yn2_r = iir->r.yn2;
|
||||||
|
|
||||||
|
output->samples = input->samples;
|
||||||
|
output->frames = input->frames;
|
||||||
|
|
||||||
|
for (i = 0; i < input->frames; i++, out += 2)
|
||||||
|
{
|
||||||
|
float in_l = out[0];
|
||||||
|
float in_r = out[1];
|
||||||
|
|
||||||
|
float l = (b0 * in_l + b1 * xn1_l + b2 * xn2_l - a1 * yn1_l - a2 * yn2_l) / a0;
|
||||||
|
float r = (b0 * in_r + b1 * xn1_r + b2 * xn2_r - a1 * yn1_r - a2 * yn2_r) / a0;
|
||||||
|
|
||||||
|
xn2_l = xn1_l;
|
||||||
|
xn1_l = in_l;
|
||||||
|
yn2_l = yn1_l;
|
||||||
|
yn1_l = l;
|
||||||
|
|
||||||
|
xn2_r = xn1_r;
|
||||||
|
xn1_r = in_r;
|
||||||
|
yn2_r = yn1_r;
|
||||||
|
yn1_r = r;
|
||||||
|
|
||||||
|
out[0] = l;
|
||||||
|
out[1] = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
iir->l.xn1 = xn1_l;
|
||||||
|
iir->l.xn2 = xn2_l;
|
||||||
|
iir->l.yn1 = yn1_l;
|
||||||
|
iir->l.yn2 = yn2_l;
|
||||||
|
|
||||||
|
iir->r.xn1 = xn1_r;
|
||||||
|
iir->r.xn2 = xn2_r;
|
||||||
|
iir->r.yn1 = yn1_r;
|
||||||
|
iir->r.yn2 = yn2_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHECK(x) if (string_is_equal(str, #x)) return x
|
||||||
|
static enum IIRFilter str_to_type(const char *str)
|
||||||
|
{
|
||||||
|
CHECK(LPF);
|
||||||
|
CHECK(HPF);
|
||||||
|
CHECK(BPCSGF);
|
||||||
|
CHECK(BPZPGF);
|
||||||
|
CHECK(APF);
|
||||||
|
CHECK(NOTCH);
|
||||||
|
CHECK(RIAA_phono);
|
||||||
|
CHECK(PEQ);
|
||||||
|
CHECK(BBOOST);
|
||||||
|
CHECK(LSH);
|
||||||
|
CHECK(HSH);
|
||||||
|
CHECK(RIAA_CD);
|
||||||
|
|
||||||
|
return LPF; /* Fallback. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void make_poly_from_roots(
|
||||||
|
const double *roots, unsigned num_roots, float *poly)
|
||||||
|
{
|
||||||
|
unsigned i, j;
|
||||||
|
|
||||||
|
poly[0] = 1;
|
||||||
|
poly[1] = -roots[0];
|
||||||
|
memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly));
|
||||||
|
|
||||||
|
for (i = 1; i < num_roots; i++)
|
||||||
|
for (j = num_roots; j > 0; j--)
|
||||||
|
poly[j] -= poly[j - 1] * roots[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iir_filter_init(struct iir_data *iir,
|
||||||
|
float sample_rate, float freq, float qual, float gain, enum IIRFilter filter_type)
|
||||||
|
{
|
||||||
|
double omega = 2.0 * M_PI * freq / sample_rate;
|
||||||
|
double cs = cos(omega);
|
||||||
|
double sn = sin(omega);
|
||||||
|
double a1pha = sn / (2.0 * qual);
|
||||||
|
double A = exp(log(10.0) * gain / 40.0);
|
||||||
|
double beta = sqrt(A + A);
|
||||||
|
|
||||||
|
float b0 = 0.0, b1 = 0.0, b2 = 0.0, a0 = 0.0, a1 = 0.0, a2 = 0.0;
|
||||||
|
|
||||||
|
/* Set up filter coefficients according to type */
|
||||||
|
switch (filter_type)
|
||||||
|
{
|
||||||
|
case LPF:
|
||||||
|
b0 = (1.0 - cs) / 2.0;
|
||||||
|
b1 = 1.0 - cs ;
|
||||||
|
b2 = (1.0 - cs) / 2.0;
|
||||||
|
a0 = 1.0 + a1pha;
|
||||||
|
a1 = -2.0 * cs;
|
||||||
|
a2 = 1.0 - a1pha;
|
||||||
|
break;
|
||||||
|
case HPF:
|
||||||
|
b0 = (1.0 + cs) / 2.0;
|
||||||
|
b1 = -(1.0 + cs);
|
||||||
|
b2 = (1.0 + cs) / 2.0;
|
||||||
|
a0 = 1.0 + a1pha;
|
||||||
|
a1 = -2.0 * cs;
|
||||||
|
a2 = 1.0 - a1pha;
|
||||||
|
break;
|
||||||
|
case APF:
|
||||||
|
b0 = 1.0 - a1pha;
|
||||||
|
b1 = -2.0 * cs;
|
||||||
|
b2 = 1.0 + a1pha;
|
||||||
|
a0 = 1.0 + a1pha;
|
||||||
|
a1 = -2.0 * cs;
|
||||||
|
a2 = 1.0 - a1pha;
|
||||||
|
break;
|
||||||
|
case BPZPGF:
|
||||||
|
b0 = a1pha;
|
||||||
|
b1 = 0.0;
|
||||||
|
b2 = -a1pha;
|
||||||
|
a0 = 1.0 + a1pha;
|
||||||
|
a1 = -2.0 * cs;
|
||||||
|
a2 = 1.0 - a1pha;
|
||||||
|
break;
|
||||||
|
case BPCSGF:
|
||||||
|
b0 = sn / 2.0;
|
||||||
|
b1 = 0.0;
|
||||||
|
b2 = -sn / 2.0;
|
||||||
|
a0 = 1.0 + a1pha;
|
||||||
|
a1 = -2.0 * cs;
|
||||||
|
a2 = 1.0 - a1pha;
|
||||||
|
break;
|
||||||
|
case NOTCH:
|
||||||
|
b0 = 1.0;
|
||||||
|
b1 = -2.0 * cs;
|
||||||
|
b2 = 1.0;
|
||||||
|
a0 = 1.0 + a1pha;
|
||||||
|
a1 = -2.0 * cs;
|
||||||
|
a2 = 1.0 - a1pha;
|
||||||
|
break;
|
||||||
|
case RIAA_phono: /* http://www.dsprelated.com/showmessage/73300/3.php */
|
||||||
|
{
|
||||||
|
double y, b_re, a_re, b_im, a_im, g;
|
||||||
|
float b[3], a[3];
|
||||||
|
|
||||||
|
if ((int)sample_rate == 44100)
|
||||||
|
{
|
||||||
|
static const double zeros[] = {-0.2014898, 0.9233820};
|
||||||
|
static const double poles[] = {0.7083149, 0.9924091};
|
||||||
|
make_poly_from_roots(zeros, 2, b);
|
||||||
|
make_poly_from_roots(poles, 2, a);
|
||||||
|
}
|
||||||
|
else if ((int)sample_rate == 48000)
|
||||||
|
{
|
||||||
|
static const double zeros[] = {-0.1766069, 0.9321590};
|
||||||
|
static const double poles[] = {0.7396325, 0.9931330};
|
||||||
|
make_poly_from_roots(zeros, 2, b);
|
||||||
|
make_poly_from_roots(poles, 2, a);
|
||||||
|
}
|
||||||
|
else if ((int)sample_rate == 88200)
|
||||||
|
{
|
||||||
|
static const double zeros[] = {-0.1168735, 0.9648312};
|
||||||
|
static const double poles[] = {0.8590646, 0.9964002};
|
||||||
|
make_poly_from_roots(zeros, 2, b);
|
||||||
|
make_poly_from_roots(poles, 2, a);
|
||||||
|
}
|
||||||
|
else if ((int)sample_rate == 96000)
|
||||||
|
{
|
||||||
|
static const double zeros[] = {-0.1141486, 0.9676817};
|
||||||
|
static const double poles[] = {0.8699137, 0.9966946};
|
||||||
|
make_poly_from_roots(zeros, 2, b);
|
||||||
|
make_poly_from_roots(poles, 2, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
b0 = b[0];
|
||||||
|
b1 = b[1];
|
||||||
|
b2 = b[2];
|
||||||
|
a0 = a[0];
|
||||||
|
a1 = a[1];
|
||||||
|
a2 = a[2];
|
||||||
|
|
||||||
|
|
||||||
|
/* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */
|
||||||
|
y = 2.0 * M_PI * 1000.0 / sample_rate;
|
||||||
|
b_re = b0 + b1 * cos(-y) + b2 * cos(-2.0 * y);
|
||||||
|
a_re = a0 + a1 * cos(-y) + a2 * cos(-2.0 * y);
|
||||||
|
b_im = b1 * sin(-y) + b2 * sin(-2.0 * y);
|
||||||
|
a_im = a1 * sin(-y) + a2 * sin(-2.0 * y);
|
||||||
|
g = 1.0 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im)));
|
||||||
|
b0 *= g; b1 *= g; b2 *= g;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PEQ:
|
||||||
|
b0 = 1.0 + a1pha * A;
|
||||||
|
b1 = -2.0 * cs;
|
||||||
|
b2 = 1.0 - a1pha * A;
|
||||||
|
a0 = 1.0 + a1pha / A;
|
||||||
|
a1 = -2.0 * cs;
|
||||||
|
a2 = 1.0 - a1pha / A;
|
||||||
|
break;
|
||||||
|
case BBOOST:
|
||||||
|
beta = sqrt((A * A + 1) / 1.0 - (pow((A - 1), 2)));
|
||||||
|
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
|
||||||
|
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
|
||||||
|
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
|
||||||
|
a0 = ((A + 1) + (A - 1) * cs + beta * sn);
|
||||||
|
a1 = -2 * ((A - 1) + (A + 1) * cs);
|
||||||
|
a2 = (A + 1) + (A - 1) * cs - beta * sn;
|
||||||
|
break;
|
||||||
|
case LSH:
|
||||||
|
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
|
||||||
|
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
|
||||||
|
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
|
||||||
|
a0 = (A + 1) + (A - 1) * cs + beta * sn;
|
||||||
|
a1 = -2 * ((A - 1) + (A + 1) * cs);
|
||||||
|
a2 = (A + 1) + (A - 1) * cs - beta * sn;
|
||||||
|
break;
|
||||||
|
case RIAA_CD:
|
||||||
|
omega = 2.0 * M_PI * 5283.0 / sample_rate;
|
||||||
|
cs = cos(omega);
|
||||||
|
sn = sin(omega);
|
||||||
|
a1pha = sn / (2.0 * 0.4845);
|
||||||
|
A = exp(log(10.0) * -9.477 / 40.0);
|
||||||
|
beta = sqrt(A + A);
|
||||||
|
(void)a1pha;
|
||||||
|
case HSH:
|
||||||
|
b0 = A * ((A + 1.0) + (A - 1.0) * cs + beta * sn);
|
||||||
|
b1 = -2.0 * A * ((A - 1.0) + (A + 1.0) * cs);
|
||||||
|
b2 = A * ((A + 1.0) + (A - 1.0) * cs - beta * sn);
|
||||||
|
a0 = (A + 1.0) - (A - 1.0) * cs + beta * sn;
|
||||||
|
a1 = 2.0 * ((A - 1.0) - (A + 1.0) * cs);
|
||||||
|
a2 = (A + 1.0) - (A - 1.0) * cs - beta * sn;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iir->b0 = b0;
|
||||||
|
iir->b1 = b1;
|
||||||
|
iir->b2 = b2;
|
||||||
|
iir->a0 = a0;
|
||||||
|
iir->a1 = a1;
|
||||||
|
iir->a2 = a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *iir_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
float freq, qual, gain;
|
||||||
|
enum IIRFilter filter = LPF;
|
||||||
|
char *type = NULL;
|
||||||
|
struct iir_data *iir = (struct iir_data*)calloc(1, sizeof(*iir));
|
||||||
|
if (!iir)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
config->get_float(userdata, "frequency", &freq, 1024.0f);
|
||||||
|
config->get_float(userdata, "quality", &qual, 0.707f);
|
||||||
|
config->get_float(userdata, "gain", &gain, 0.0f);
|
||||||
|
|
||||||
|
config->get_string(userdata, "type", &type, "LPF");
|
||||||
|
|
||||||
|
filter = str_to_type(type);
|
||||||
|
config->free(type);
|
||||||
|
|
||||||
|
iir_filter_init(iir, info->input_rate, freq, qual, gain, filter);
|
||||||
|
return iir;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation iir_plug = {
|
||||||
|
iir_init,
|
||||||
|
iir_process,
|
||||||
|
iir_free,
|
||||||
|
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"IIR",
|
||||||
|
"iir",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation iir_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &iir_plug;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
global: dspfilter_get_implementation;
|
||||||
|
local: *;
|
||||||
|
};
|
|
@ -0,0 +1,112 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (panning.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
struct panning_data
|
||||||
|
{
|
||||||
|
float left[2];
|
||||||
|
float right[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void panning_free(void *data)
|
||||||
|
{
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void panning_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct panning_data *pan = (struct panning_data*)data;
|
||||||
|
float *out = output->samples;
|
||||||
|
|
||||||
|
output->samples = input->samples;
|
||||||
|
output->frames = input->frames;
|
||||||
|
|
||||||
|
for (i = 0; i < input->frames; i++, out += 2)
|
||||||
|
{
|
||||||
|
float left = out[0];
|
||||||
|
float right = out[1];
|
||||||
|
out[0] = left * pan->left[0] + right * pan->left[1];
|
||||||
|
out[1] = left * pan->right[0] + right * pan->right[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *panning_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
static const float default_left[] = { 1.0f, 0.0f };
|
||||||
|
static const float default_right[] = { 0.0f, 1.0f };
|
||||||
|
float *left = NULL;
|
||||||
|
float *right = NULL;
|
||||||
|
unsigned num_left = 0;
|
||||||
|
unsigned num_right = 0;
|
||||||
|
struct panning_data *pan = (struct panning_data*)
|
||||||
|
calloc(1, sizeof(*pan));
|
||||||
|
|
||||||
|
if (!pan)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
config->get_float_array(userdata, "left_mix",
|
||||||
|
&left, &num_left, default_left, 2);
|
||||||
|
config->get_float_array(userdata, "right_mix",
|
||||||
|
&right, &num_right, default_right, 2);
|
||||||
|
|
||||||
|
memcpy(pan->left, (num_left == 2) ?
|
||||||
|
left : default_left, sizeof(pan->left));
|
||||||
|
memcpy(pan->right, (num_right == 2) ?
|
||||||
|
right : default_right, sizeof(pan->right));
|
||||||
|
|
||||||
|
config->free(left);
|
||||||
|
config->free(right);
|
||||||
|
|
||||||
|
return pan;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation panning = {
|
||||||
|
panning_init,
|
||||||
|
panning_process,
|
||||||
|
panning_free,
|
||||||
|
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"Panning",
|
||||||
|
"panning",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation panning_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *
|
||||||
|
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &panning;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (phaser.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
#define phaserlfoshape 4.0
|
||||||
|
#define phaserlfoskipsamples 20
|
||||||
|
|
||||||
|
struct phaser_data
|
||||||
|
{
|
||||||
|
float freq;
|
||||||
|
float startphase;
|
||||||
|
float fb;
|
||||||
|
float depth;
|
||||||
|
float drywet;
|
||||||
|
float old[2][24];
|
||||||
|
float gain;
|
||||||
|
float fbout[2];
|
||||||
|
float lfoskip;
|
||||||
|
float phase;
|
||||||
|
|
||||||
|
int stages;
|
||||||
|
unsigned long skipcount;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void phaser_free(void *data)
|
||||||
|
{
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void phaser_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
unsigned i, c;
|
||||||
|
int s;
|
||||||
|
float m[2], tmp[2];
|
||||||
|
struct phaser_data *ph = (struct phaser_data*)data;
|
||||||
|
float *out = output->samples;
|
||||||
|
|
||||||
|
output->samples = input->samples;
|
||||||
|
output->frames = input->frames;
|
||||||
|
|
||||||
|
for (i = 0; i < input->frames; i++, out += 2)
|
||||||
|
{
|
||||||
|
float in[2] = { out[0], out[1] };
|
||||||
|
|
||||||
|
for (c = 0; c < 2; c++)
|
||||||
|
m[c] = in[c] + ph->fbout[c] * ph->fb * 0.01f;
|
||||||
|
|
||||||
|
if ((ph->skipcount++ % phaserlfoskipsamples) == 0)
|
||||||
|
{
|
||||||
|
ph->gain = 0.5 * (1.0 + cos(ph->skipcount * ph->lfoskip + ph->phase));
|
||||||
|
ph->gain = (exp(ph->gain * phaserlfoshape) - 1.0) / (exp(phaserlfoshape) - 1);
|
||||||
|
ph->gain = 1.0 - ph->gain * ph->depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (s = 0; s < ph->stages; s++)
|
||||||
|
{
|
||||||
|
for (c = 0; c < 2; c++)
|
||||||
|
{
|
||||||
|
tmp[c] = ph->old[c][s];
|
||||||
|
ph->old[c][s] = ph->gain * tmp[c] + m[c];
|
||||||
|
m[c] = tmp[c] - ph->gain * ph->old[c][s];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (c = 0; c < 2; c++)
|
||||||
|
{
|
||||||
|
ph->fbout[c] = m[c];
|
||||||
|
out[c] = m[c] * ph->drywet + in[c] * (1.0f - ph->drywet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *phaser_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
float lfo_freq, lfo_start_phase;
|
||||||
|
struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph));
|
||||||
|
if (!ph)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
config->get_float(userdata, "lfo_freq", &lfo_freq, 0.4f);
|
||||||
|
config->get_float(userdata, "lfo_start_phase", &lfo_start_phase, 0.0f);
|
||||||
|
config->get_float(userdata, "feedback", &ph->fb, 0.0f);
|
||||||
|
config->get_float(userdata, "depth", &ph->depth, 0.4f);
|
||||||
|
config->get_float(userdata, "dry_wet", &ph->drywet, 0.5f);
|
||||||
|
config->get_int(userdata, "stages", &ph->stages, 2);
|
||||||
|
|
||||||
|
if (ph->stages < 1)
|
||||||
|
ph->stages = 1;
|
||||||
|
else if (ph->stages > 24)
|
||||||
|
ph->stages = 24;
|
||||||
|
|
||||||
|
ph->lfoskip = lfo_freq * 2.0 * M_PI / info->input_rate;
|
||||||
|
ph->phase = lfo_start_phase * M_PI / 180.0;
|
||||||
|
|
||||||
|
return ph;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation phaser_plug = {
|
||||||
|
phaser_init,
|
||||||
|
phaser_process,
|
||||||
|
phaser_free,
|
||||||
|
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"Phaser",
|
||||||
|
"phaser",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation phaser_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &phaser_plug;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
|
@ -0,0 +1,327 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (reverb.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_inline.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
struct comb
|
||||||
|
{
|
||||||
|
float *buffer;
|
||||||
|
unsigned bufsize;
|
||||||
|
unsigned bufidx;
|
||||||
|
|
||||||
|
float feedback;
|
||||||
|
float filterstore;
|
||||||
|
float damp1, damp2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct allpass
|
||||||
|
{
|
||||||
|
float *buffer;
|
||||||
|
float feedback;
|
||||||
|
unsigned bufsize;
|
||||||
|
unsigned bufidx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static INLINE float comb_process(struct comb *c, float input)
|
||||||
|
{
|
||||||
|
float output = c->buffer[c->bufidx];
|
||||||
|
c->filterstore = (output * c->damp2) + (c->filterstore * c->damp1);
|
||||||
|
|
||||||
|
c->buffer[c->bufidx] = input + (c->filterstore * c->feedback);
|
||||||
|
|
||||||
|
c->bufidx++;
|
||||||
|
if (c->bufidx >= c->bufsize)
|
||||||
|
c->bufidx = 0;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static INLINE float allpass_process(struct allpass *a, float input)
|
||||||
|
{
|
||||||
|
float bufout = a->buffer[a->bufidx];
|
||||||
|
float output = -input + bufout;
|
||||||
|
a->buffer[a->bufidx] = input + bufout * a->feedback;
|
||||||
|
|
||||||
|
a->bufidx++;
|
||||||
|
if (a->bufidx >= a->bufsize)
|
||||||
|
a->bufidx = 0;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define numcombs 8
|
||||||
|
#define numallpasses 4
|
||||||
|
static const float muted = 0;
|
||||||
|
static const float fixedgain = 0.015f;
|
||||||
|
static const float scalewet = 3;
|
||||||
|
static const float scaledry = 2;
|
||||||
|
static const float scaledamp = 0.4f;
|
||||||
|
static const float scaleroom = 0.28f;
|
||||||
|
static const float offsetroom = 0.7f;
|
||||||
|
static const float initialroom = 0.5f;
|
||||||
|
static const float initialdamp = 0.5f;
|
||||||
|
static const float initialwet = 1.0f / 3.0f;
|
||||||
|
static const float initialdry = 0;
|
||||||
|
static const float initialwidth = 1;
|
||||||
|
static const float initialmode = 0;
|
||||||
|
static const float freezemode = 0.5f;
|
||||||
|
|
||||||
|
#define combtuningL1 1116
|
||||||
|
#define combtuningL2 1188
|
||||||
|
#define combtuningL3 1277
|
||||||
|
#define combtuningL4 1356
|
||||||
|
#define combtuningL5 1422
|
||||||
|
#define combtuningL6 1491
|
||||||
|
#define combtuningL7 1557
|
||||||
|
#define combtuningL8 1617
|
||||||
|
#define allpasstuningL1 556
|
||||||
|
#define allpasstuningL2 441
|
||||||
|
#define allpasstuningL3 341
|
||||||
|
#define allpasstuningL4 225
|
||||||
|
|
||||||
|
struct revmodel
|
||||||
|
{
|
||||||
|
struct comb combL[numcombs];
|
||||||
|
struct allpass allpassL[numallpasses];
|
||||||
|
|
||||||
|
float bufcombL1[combtuningL1];
|
||||||
|
float bufcombL2[combtuningL2];
|
||||||
|
float bufcombL3[combtuningL3];
|
||||||
|
float bufcombL4[combtuningL4];
|
||||||
|
float bufcombL5[combtuningL5];
|
||||||
|
float bufcombL6[combtuningL6];
|
||||||
|
float bufcombL7[combtuningL7];
|
||||||
|
float bufcombL8[combtuningL8];
|
||||||
|
|
||||||
|
float bufallpassL1[allpasstuningL1];
|
||||||
|
float bufallpassL2[allpasstuningL2];
|
||||||
|
float bufallpassL3[allpasstuningL3];
|
||||||
|
float bufallpassL4[allpasstuningL4];
|
||||||
|
|
||||||
|
float gain;
|
||||||
|
float roomsize, roomsize1;
|
||||||
|
float damp, damp1;
|
||||||
|
float wet, wet1, wet2;
|
||||||
|
float dry;
|
||||||
|
float width;
|
||||||
|
float mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static float revmodel_process(struct revmodel *rev, float in)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float mono_out = 0.0f;
|
||||||
|
float mono_in = in;
|
||||||
|
float input = mono_in * rev->gain;
|
||||||
|
|
||||||
|
for (i = 0; i < numcombs; i++)
|
||||||
|
mono_out += comb_process(&rev->combL[i], input);
|
||||||
|
|
||||||
|
for (i = 0; i < numallpasses; i++)
|
||||||
|
mono_out = allpass_process(&rev->allpassL[i], mono_out);
|
||||||
|
|
||||||
|
return mono_in * rev->dry + mono_out * rev->wet1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revmodel_update(struct revmodel *rev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f);
|
||||||
|
|
||||||
|
if (rev->mode >= freezemode)
|
||||||
|
{
|
||||||
|
rev->roomsize1 = 1.0f;
|
||||||
|
rev->damp1 = 0.0f;
|
||||||
|
rev->gain = muted;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rev->roomsize1 = rev->roomsize;
|
||||||
|
rev->damp1 = rev->damp;
|
||||||
|
rev->gain = fixedgain;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < numcombs; i++)
|
||||||
|
{
|
||||||
|
rev->combL[i].feedback = rev->roomsize1;
|
||||||
|
rev->combL[i].damp1 = rev->damp1;
|
||||||
|
rev->combL[i].damp2 = 1.0f - rev->damp1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revmodel_setroomsize(struct revmodel *rev, float value)
|
||||||
|
{
|
||||||
|
rev->roomsize = value * scaleroom + offsetroom;
|
||||||
|
revmodel_update(rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revmodel_setdamp(struct revmodel *rev, float value)
|
||||||
|
{
|
||||||
|
rev->damp = value * scaledamp;
|
||||||
|
revmodel_update(rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revmodel_setwet(struct revmodel *rev, float value)
|
||||||
|
{
|
||||||
|
rev->wet = value * scalewet;
|
||||||
|
revmodel_update(rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revmodel_setdry(struct revmodel *rev, float value)
|
||||||
|
{
|
||||||
|
rev->dry = value * scaledry;
|
||||||
|
revmodel_update(rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revmodel_setwidth(struct revmodel *rev, float value)
|
||||||
|
{
|
||||||
|
rev->width = value;
|
||||||
|
revmodel_update(rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revmodel_setmode(struct revmodel *rev, float value)
|
||||||
|
{
|
||||||
|
rev->mode = value;
|
||||||
|
revmodel_update(rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revmodel_init(struct revmodel *rev)
|
||||||
|
{
|
||||||
|
rev->combL[0].buffer = rev->bufcombL1; rev->combL[0].bufsize = combtuningL1;
|
||||||
|
rev->combL[1].buffer = rev->bufcombL2; rev->combL[1].bufsize = combtuningL2;
|
||||||
|
rev->combL[2].buffer = rev->bufcombL3; rev->combL[2].bufsize = combtuningL3;
|
||||||
|
rev->combL[3].buffer = rev->bufcombL4; rev->combL[3].bufsize = combtuningL4;
|
||||||
|
rev->combL[4].buffer = rev->bufcombL5; rev->combL[4].bufsize = combtuningL5;
|
||||||
|
rev->combL[5].buffer = rev->bufcombL6; rev->combL[5].bufsize = combtuningL6;
|
||||||
|
rev->combL[6].buffer = rev->bufcombL7; rev->combL[6].bufsize = combtuningL7;
|
||||||
|
rev->combL[7].buffer = rev->bufcombL8; rev->combL[7].bufsize = combtuningL8;
|
||||||
|
|
||||||
|
rev->allpassL[0].buffer = rev->bufallpassL1; rev->allpassL[0].bufsize = allpasstuningL1;
|
||||||
|
rev->allpassL[1].buffer = rev->bufallpassL2; rev->allpassL[1].bufsize = allpasstuningL2;
|
||||||
|
rev->allpassL[2].buffer = rev->bufallpassL3; rev->allpassL[2].bufsize = allpasstuningL3;
|
||||||
|
rev->allpassL[3].buffer = rev->bufallpassL4; rev->allpassL[3].bufsize = allpasstuningL4;
|
||||||
|
|
||||||
|
rev->allpassL[0].feedback = 0.5f;
|
||||||
|
rev->allpassL[1].feedback = 0.5f;
|
||||||
|
rev->allpassL[2].feedback = 0.5f;
|
||||||
|
rev->allpassL[3].feedback = 0.5f;
|
||||||
|
|
||||||
|
revmodel_setwet(rev, initialwet);
|
||||||
|
revmodel_setroomsize(rev, initialroom);
|
||||||
|
revmodel_setdry(rev, initialdry);
|
||||||
|
revmodel_setdamp(rev, initialdamp);
|
||||||
|
revmodel_setwidth(rev, initialwidth);
|
||||||
|
revmodel_setmode(rev, initialmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct reverb_data
|
||||||
|
{
|
||||||
|
struct revmodel left, right;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void reverb_free(void *data)
|
||||||
|
{
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reverb_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
float *out;
|
||||||
|
struct reverb_data *rev = (struct reverb_data*)data;
|
||||||
|
|
||||||
|
output->samples = input->samples;
|
||||||
|
output->frames = input->frames;
|
||||||
|
out = output->samples;
|
||||||
|
|
||||||
|
for (i = 0; i < input->frames; i++, out += 2)
|
||||||
|
{
|
||||||
|
float in[2] = { out[0], out[1] };
|
||||||
|
|
||||||
|
out[0] = revmodel_process(&rev->left, in[0]);
|
||||||
|
out[1] = revmodel_process(&rev->right, in[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *reverb_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
float drytime, wettime, damping, roomwidth, roomsize;
|
||||||
|
struct reverb_data *rev = (struct reverb_data*)
|
||||||
|
calloc(1, sizeof(*rev));
|
||||||
|
if (!rev)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
config->get_float(userdata, "drytime", &drytime, 0.43f);
|
||||||
|
config->get_float(userdata, "wettime", &wettime, 0.4f);
|
||||||
|
config->get_float(userdata, "damping", &damping, 0.8f);
|
||||||
|
config->get_float(userdata, "roomwidth", &roomwidth, 0.56f);
|
||||||
|
config->get_float(userdata, "roomsize", &roomsize, 0.56f);
|
||||||
|
|
||||||
|
revmodel_init(&rev->left);
|
||||||
|
revmodel_init(&rev->right);
|
||||||
|
|
||||||
|
revmodel_setdamp(&rev->left, damping);
|
||||||
|
revmodel_setdry(&rev->left, drytime);
|
||||||
|
revmodel_setwet(&rev->left, wettime);
|
||||||
|
revmodel_setwidth(&rev->left, roomwidth);
|
||||||
|
revmodel_setroomsize(&rev->left, roomsize);
|
||||||
|
|
||||||
|
revmodel_setdamp(&rev->right, damping);
|
||||||
|
revmodel_setdry(&rev->right, drytime);
|
||||||
|
revmodel_setwet(&rev->right, wettime);
|
||||||
|
revmodel_setwidth(&rev->right, roomwidth);
|
||||||
|
revmodel_setroomsize(&rev->right, roomsize);
|
||||||
|
|
||||||
|
return rev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation reverb_plug = {
|
||||||
|
reverb_init,
|
||||||
|
reverb_process,
|
||||||
|
reverb_free,
|
||||||
|
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"Reverb",
|
||||||
|
"reverb",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation reverb_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &reverb_plug;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (wahwah.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <libretro_dspfilter.h>
|
||||||
|
|
||||||
|
#define WAHWAH_LFO_SKIP_SAMPLES 30
|
||||||
|
|
||||||
|
struct wahwah_data
|
||||||
|
{
|
||||||
|
float phase;
|
||||||
|
float lfoskip;
|
||||||
|
float b0, b1, b2, a0, a1, a2;
|
||||||
|
float freq, startphase;
|
||||||
|
float depth, freqofs, res;
|
||||||
|
unsigned long skipcount;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
float xn1, xn2, yn1, yn2;
|
||||||
|
} l, r;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void wahwah_free(void *data)
|
||||||
|
{
|
||||||
|
if (data)
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wahwah_process(void *data, struct dspfilter_output *output,
|
||||||
|
const struct dspfilter_input *input)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct wahwah_data *wah = (struct wahwah_data*)data;
|
||||||
|
float *out = output->samples;
|
||||||
|
|
||||||
|
output->samples = input->samples;
|
||||||
|
output->frames = input->frames;
|
||||||
|
|
||||||
|
for (i = 0; i < input->frames; i++, out += 2)
|
||||||
|
{
|
||||||
|
float out_l, out_r;
|
||||||
|
float in[2] = { out[0], out[1] };
|
||||||
|
|
||||||
|
if ((wah->skipcount++ % WAHWAH_LFO_SKIP_SAMPLES) == 0)
|
||||||
|
{
|
||||||
|
float omega, sn, cs, alpha;
|
||||||
|
float frequency = (1.0 + cos(wah->skipcount * wah->lfoskip + wah->phase)) / 2.0;
|
||||||
|
|
||||||
|
frequency = frequency * wah->depth * (1.0 - wah->freqofs) + wah->freqofs;
|
||||||
|
frequency = exp((frequency - 1.0) * 6.0);
|
||||||
|
|
||||||
|
omega = M_PI * frequency;
|
||||||
|
sn = sin(omega);
|
||||||
|
cs = cos(omega);
|
||||||
|
alpha = sn / (2.0 * wah->res);
|
||||||
|
|
||||||
|
wah->b0 = (1.0 - cs) / 2.0;
|
||||||
|
wah->b1 = 1.0 - cs;
|
||||||
|
wah->b2 = (1.0 - cs) / 2.0;
|
||||||
|
wah->a0 = 1.0 + alpha;
|
||||||
|
wah->a1 = -2.0 * cs;
|
||||||
|
wah->a2 = 1.0 - alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_l = (wah->b0 * in[0] + wah->b1 * wah->l.xn1 + wah->b2 * wah->l.xn2 - wah->a1 * wah->l.yn1 - wah->a2 * wah->l.yn2) / wah->a0;
|
||||||
|
out_r = (wah->b0 * in[1] + wah->b1 * wah->r.xn1 + wah->b2 * wah->r.xn2 - wah->a1 * wah->r.yn1 - wah->a2 * wah->r.yn2) / wah->a0;
|
||||||
|
|
||||||
|
wah->l.xn2 = wah->l.xn1;
|
||||||
|
wah->l.xn1 = in[0];
|
||||||
|
wah->l.yn2 = wah->l.yn1;
|
||||||
|
wah->l.yn1 = out_l;
|
||||||
|
|
||||||
|
wah->r.xn2 = wah->r.xn1;
|
||||||
|
wah->r.xn1 = in[1];
|
||||||
|
wah->r.yn2 = wah->r.yn1;
|
||||||
|
wah->r.yn1 = out_r;
|
||||||
|
|
||||||
|
out[0] = out_l;
|
||||||
|
out[1] = out_r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *wahwah_init(const struct dspfilter_info *info,
|
||||||
|
const struct dspfilter_config *config, void *userdata)
|
||||||
|
{
|
||||||
|
struct wahwah_data *wah = (struct wahwah_data*)calloc(1, sizeof(*wah));
|
||||||
|
if (!wah)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
config->get_float(userdata, "lfo_freq", &wah->freq, 1.5f);
|
||||||
|
config->get_float(userdata, "lfo_start_phase", &wah->startphase, 0.0f);
|
||||||
|
config->get_float(userdata, "freq_offset", &wah->freqofs, 0.3f);
|
||||||
|
config->get_float(userdata, "depth", &wah->depth, 0.7f);
|
||||||
|
config->get_float(userdata, "resonance", &wah->res, 2.5f);
|
||||||
|
|
||||||
|
wah->lfoskip = wah->freq * 2.0 * M_PI / info->input_rate;
|
||||||
|
wah->phase = wah->startphase * M_PI / 180.0;
|
||||||
|
|
||||||
|
return wah;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dspfilter_implementation wahwah_plug = {
|
||||||
|
wahwah_init,
|
||||||
|
wahwah_process,
|
||||||
|
wahwah_free,
|
||||||
|
|
||||||
|
DSPFILTER_API_VERSION,
|
||||||
|
"Wah-Wah",
|
||||||
|
"wahwah",
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_FILTERS_BUILTIN
|
||||||
|
#define dspfilter_get_implementation wahwah_dspfilter_get_implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct dspfilter_implementation *
|
||||||
|
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
(void)mask;
|
||||||
|
return &wahwah_plug;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef dspfilter_get_implementation
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (audio_resampler.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
|
||||||
|
#include <string/stdstring.h>
|
||||||
|
#include <features/features_cpu.h>
|
||||||
|
#include <file/config_file_userdata.h>
|
||||||
|
|
||||||
|
#include <audio/audio_resampler.h>
|
||||||
|
|
||||||
|
static const retro_resampler_t *resampler_drivers[] = {
|
||||||
|
&sinc_resampler,
|
||||||
|
#ifdef HAVE_CC_RESAMPLER
|
||||||
|
&CC_resampler,
|
||||||
|
#endif
|
||||||
|
&nearest_resampler,
|
||||||
|
&null_resampler,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct resampler_config resampler_config = {
|
||||||
|
config_userdata_get_float,
|
||||||
|
config_userdata_get_int,
|
||||||
|
config_userdata_get_float_array,
|
||||||
|
config_userdata_get_int_array,
|
||||||
|
config_userdata_get_string,
|
||||||
|
config_userdata_free,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find_resampler_driver_index:
|
||||||
|
* @ident : Identifier of resampler driver to find.
|
||||||
|
*
|
||||||
|
* Finds resampler driver index by @ident name.
|
||||||
|
*
|
||||||
|
* Returns: resampler driver index if resampler driver was found, otherwise
|
||||||
|
* -1.
|
||||||
|
**/
|
||||||
|
static int find_resampler_driver_index(const char *ident)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; resampler_drivers[i]; i++)
|
||||||
|
if (string_is_equal_noncase(ident, resampler_drivers[i]->ident))
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* audio_resampler_driver_find_handle:
|
||||||
|
* @idx : index of driver to get handle to.
|
||||||
|
*
|
||||||
|
* Returns: handle to audio resampler driver at index. Can be NULL
|
||||||
|
* if nothing found.
|
||||||
|
**/
|
||||||
|
const void *audio_resampler_driver_find_handle(int idx)
|
||||||
|
{
|
||||||
|
const void *drv = resampler_drivers[idx];
|
||||||
|
if (!drv)
|
||||||
|
return NULL;
|
||||||
|
return drv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* audio_resampler_driver_find_ident:
|
||||||
|
* @idx : index of driver to get handle to.
|
||||||
|
*
|
||||||
|
* Returns: Human-readable identifier of audio resampler driver at index.
|
||||||
|
* Can be NULL if nothing found.
|
||||||
|
**/
|
||||||
|
const char *audio_resampler_driver_find_ident(int idx)
|
||||||
|
{
|
||||||
|
const retro_resampler_t *drv = resampler_drivers[idx];
|
||||||
|
if (!drv)
|
||||||
|
return NULL;
|
||||||
|
return drv->ident;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find_resampler_driver:
|
||||||
|
* @ident : Identifier of resampler driver to find.
|
||||||
|
*
|
||||||
|
* Finds resampler by @ident name.
|
||||||
|
*
|
||||||
|
* Returns: resampler driver if resampler driver was found, otherwise
|
||||||
|
* NULL.
|
||||||
|
**/
|
||||||
|
static const retro_resampler_t *find_resampler_driver(const char *ident)
|
||||||
|
{
|
||||||
|
int i = find_resampler_driver_index(ident);
|
||||||
|
|
||||||
|
if (i >= 0)
|
||||||
|
return resampler_drivers[i];
|
||||||
|
|
||||||
|
return resampler_drivers[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resampler_append_plugs:
|
||||||
|
* @re : Resampler handle
|
||||||
|
* @backend : Resampler backend that is about to be set.
|
||||||
|
* @bw_ratio : Bandwidth ratio.
|
||||||
|
*
|
||||||
|
* Initializes resampler driver based on queried CPU features.
|
||||||
|
*
|
||||||
|
* Returns: true (1) if successfully initialized, otherwise false (0).
|
||||||
|
**/
|
||||||
|
static bool resampler_append_plugs(void **re,
|
||||||
|
const retro_resampler_t **backend,
|
||||||
|
enum resampler_quality quality,
|
||||||
|
double bw_ratio)
|
||||||
|
{
|
||||||
|
resampler_simd_mask_t mask = (resampler_simd_mask_t)cpu_features_get();
|
||||||
|
|
||||||
|
if (*backend)
|
||||||
|
*re = (*backend)->init(&resampler_config, bw_ratio, quality, mask);
|
||||||
|
|
||||||
|
if (!*re)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retro_resampler_realloc:
|
||||||
|
* @re : Resampler handle
|
||||||
|
* @backend : Resampler backend that is about to be set.
|
||||||
|
* @ident : Identifier name for resampler we want.
|
||||||
|
* @bw_ratio : Bandwidth ratio.
|
||||||
|
*
|
||||||
|
* Reallocates resampler. Will free previous handle before
|
||||||
|
* allocating a new one. If ident is NULL, first resampler will be used.
|
||||||
|
*
|
||||||
|
* Returns: true (1) if successful, otherwise false (0).
|
||||||
|
**/
|
||||||
|
bool retro_resampler_realloc(void **re, const retro_resampler_t **backend,
|
||||||
|
const char *ident, enum resampler_quality quality, double bw_ratio)
|
||||||
|
{
|
||||||
|
if (*re && *backend)
|
||||||
|
(*backend)->free(*re);
|
||||||
|
|
||||||
|
*re = NULL;
|
||||||
|
*backend = find_resampler_driver(ident);
|
||||||
|
|
||||||
|
if (!resampler_append_plugs(re, backend, quality, bw_ratio))
|
||||||
|
{
|
||||||
|
if (!*re)
|
||||||
|
*backend = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (nearest_resampler.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <audio/audio_resampler.h>
|
||||||
|
|
||||||
|
typedef struct rarch_nearest_resampler
|
||||||
|
{
|
||||||
|
float fraction;
|
||||||
|
} rarch_nearest_resampler_t;
|
||||||
|
|
||||||
|
static void resampler_nearest_process(
|
||||||
|
void *re_, struct resampler_data *data)
|
||||||
|
{
|
||||||
|
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
|
||||||
|
audio_frame_float_t *inp = (audio_frame_float_t*)data->data_in;
|
||||||
|
audio_frame_float_t *inp_max = (audio_frame_float_t*)inp + data->input_frames;
|
||||||
|
audio_frame_float_t *outp = (audio_frame_float_t*)data->data_out;
|
||||||
|
float ratio = 1.0 / data->ratio;
|
||||||
|
|
||||||
|
while(inp != inp_max)
|
||||||
|
{
|
||||||
|
while(re->fraction > 1)
|
||||||
|
{
|
||||||
|
*outp++ = *inp;
|
||||||
|
re->fraction -= ratio;
|
||||||
|
}
|
||||||
|
re->fraction++;
|
||||||
|
inp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->output_frames = (outp - (audio_frame_float_t*)data->data_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void resampler_nearest_free(void *re_)
|
||||||
|
{
|
||||||
|
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
|
||||||
|
if (re)
|
||||||
|
free(re);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *resampler_nearest_init(const struct resampler_config *config,
|
||||||
|
double bandwidth_mod,
|
||||||
|
enum resampler_quality quality,
|
||||||
|
resampler_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)
|
||||||
|
calloc(1, sizeof(rarch_nearest_resampler_t));
|
||||||
|
|
||||||
|
(void)config;
|
||||||
|
(void)mask;
|
||||||
|
|
||||||
|
if (!re)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
re->fraction = 0;
|
||||||
|
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
retro_resampler_t nearest_resampler = {
|
||||||
|
resampler_nearest_init,
|
||||||
|
resampler_nearest_process,
|
||||||
|
resampler_nearest_free,
|
||||||
|
RESAMPLER_API_VERSION,
|
||||||
|
"nearest",
|
||||||
|
"nearest"
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (null_resampler.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <audio/audio_resampler.h>
|
||||||
|
|
||||||
|
typedef struct rarch_null_resampler
|
||||||
|
{
|
||||||
|
void *empty;
|
||||||
|
} rarch_null_resampler_t;
|
||||||
|
|
||||||
|
static void resampler_null_process(
|
||||||
|
void *re_, struct resampler_data *data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void resampler_null_free(void *re_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *resampler_null_init(const struct resampler_config *config,
|
||||||
|
double bandwidth_mod,
|
||||||
|
enum resampler_quality quality,
|
||||||
|
resampler_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
return (void*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
retro_resampler_t null_resampler = {
|
||||||
|
resampler_null_init,
|
||||||
|
resampler_null_process,
|
||||||
|
resampler_null_free,
|
||||||
|
RESAMPLER_API_VERSION,
|
||||||
|
"null",
|
||||||
|
"null"
|
||||||
|
};
|
|
@ -0,0 +1,720 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (sinc_resampler.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Bog-standard windowed SINC implementation. */
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_inline.h>
|
||||||
|
#include <filters.h>
|
||||||
|
#include <memalign.h>
|
||||||
|
|
||||||
|
#include <audio/audio_resampler.h>
|
||||||
|
#include <filters.h>
|
||||||
|
|
||||||
|
#ifdef __SSE__
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__AVX__)
|
||||||
|
#include <immintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Rough SNR values for upsampling:
|
||||||
|
* LOWEST: 40 dB
|
||||||
|
* LOWER: 55 dB
|
||||||
|
* NORMAL: 70 dB
|
||||||
|
* HIGHER: 110 dB
|
||||||
|
* HIGHEST: 140 dB
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TODO, make all this more configurable. */
|
||||||
|
|
||||||
|
enum sinc_window
|
||||||
|
{
|
||||||
|
SINC_WINDOW_NONE = 0,
|
||||||
|
SINC_WINDOW_KAISER,
|
||||||
|
SINC_WINDOW_LANCZOS
|
||||||
|
};
|
||||||
|
|
||||||
|
/* For the little amount of taps we're using,
|
||||||
|
* SSE1 is faster than AVX for some reason.
|
||||||
|
* AVX code is kept here though as by increasing number
|
||||||
|
* of sinc taps, the AVX code is clearly faster than SSE1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct rarch_sinc_resampler
|
||||||
|
{
|
||||||
|
unsigned enable_avx;
|
||||||
|
unsigned phase_bits;
|
||||||
|
unsigned subphase_bits;
|
||||||
|
unsigned subphase_mask;
|
||||||
|
unsigned taps;
|
||||||
|
unsigned ptr;
|
||||||
|
uint32_t time;
|
||||||
|
float subphase_mod;
|
||||||
|
float kaiser_beta;
|
||||||
|
enum sinc_window window_type;
|
||||||
|
|
||||||
|
/* A buffer for phase_table, buffer_l and buffer_r
|
||||||
|
* are created in a single calloc().
|
||||||
|
* Ensure that we get as good cache locality as we can hope for. */
|
||||||
|
float *main_buffer;
|
||||||
|
float *phase_table;
|
||||||
|
float *buffer_l;
|
||||||
|
float *buffer_r;
|
||||||
|
} rarch_sinc_resampler_t;
|
||||||
|
|
||||||
|
#if defined(__ARM_NEON__)
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
#else
|
||||||
|
#define WANT_NEON
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WANT_NEON
|
||||||
|
/* Assumes that taps >= 8, and that taps is a multiple of 8. */
|
||||||
|
void process_sinc_neon_asm(float *out, const float *left,
|
||||||
|
const float *right, const float *coeff, unsigned taps);
|
||||||
|
|
||||||
|
static void resampler_sinc_process_neon(void *re_, struct resampler_data *data)
|
||||||
|
{
|
||||||
|
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
|
||||||
|
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
|
||||||
|
|
||||||
|
uint32_t ratio = phases / data->ratio;
|
||||||
|
const float *input = data->data_in;
|
||||||
|
float *output = data->data_out;
|
||||||
|
size_t frames = data->input_frames;
|
||||||
|
size_t out_frames = 0;
|
||||||
|
|
||||||
|
while (frames)
|
||||||
|
{
|
||||||
|
while (frames && resamp->time >= phases)
|
||||||
|
{
|
||||||
|
/* Push in reverse to make filter more obvious. */
|
||||||
|
if (!resamp->ptr)
|
||||||
|
resamp->ptr = resamp->taps;
|
||||||
|
resamp->ptr--;
|
||||||
|
|
||||||
|
resamp->buffer_l[resamp->ptr + resamp->taps] =
|
||||||
|
resamp->buffer_l[resamp->ptr] = *input++;
|
||||||
|
|
||||||
|
resamp->buffer_r[resamp->ptr + resamp->taps] =
|
||||||
|
resamp->buffer_r[resamp->ptr] = *input++;
|
||||||
|
|
||||||
|
resamp->time -= phases;
|
||||||
|
frames--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (resamp->time < phases)
|
||||||
|
{
|
||||||
|
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||||
|
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||||
|
unsigned taps = resamp->taps;
|
||||||
|
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||||
|
const float *phase_table = resamp->phase_table + phase * taps;
|
||||||
|
|
||||||
|
process_sinc_neon_asm(output, buffer_l, buffer_r, phase_table, taps);
|
||||||
|
|
||||||
|
output += 2;
|
||||||
|
out_frames++;
|
||||||
|
resamp->time += ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data->output_frames = out_frames;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__AVX__)
|
||||||
|
static void resampler_sinc_process_avx(void *re_, struct resampler_data *data)
|
||||||
|
{
|
||||||
|
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
|
||||||
|
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
|
||||||
|
|
||||||
|
uint32_t ratio = phases / data->ratio;
|
||||||
|
const float *input = data->data_in;
|
||||||
|
float *output = data->data_out;
|
||||||
|
size_t frames = data->input_frames;
|
||||||
|
size_t out_frames = 0;
|
||||||
|
|
||||||
|
while (frames)
|
||||||
|
{
|
||||||
|
while (frames && resamp->time >= phases)
|
||||||
|
{
|
||||||
|
/* Push in reverse to make filter more obvious. */
|
||||||
|
if (!resamp->ptr)
|
||||||
|
resamp->ptr = resamp->taps;
|
||||||
|
resamp->ptr--;
|
||||||
|
|
||||||
|
resamp->buffer_l[resamp->ptr + resamp->taps] =
|
||||||
|
resamp->buffer_l[resamp->ptr] = *input++;
|
||||||
|
|
||||||
|
resamp->buffer_r[resamp->ptr + resamp->taps] =
|
||||||
|
resamp->buffer_r[resamp->ptr] = *input++;
|
||||||
|
|
||||||
|
resamp->time -= phases;
|
||||||
|
frames--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (resamp->time < phases)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
__m256 delta, sum_l, sum_r;
|
||||||
|
float *delta_table = NULL;
|
||||||
|
float *phase_table = NULL;
|
||||||
|
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||||
|
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||||
|
unsigned taps = resamp->taps;
|
||||||
|
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||||
|
|
||||||
|
phase_table = resamp->phase_table + phase * taps;
|
||||||
|
|
||||||
|
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||||
|
{
|
||||||
|
phase_table = resamp->phase_table + phase * taps * 2;
|
||||||
|
delta_table = phase_table + taps;
|
||||||
|
delta = _mm256_set1_ps((float)
|
||||||
|
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_l = _mm256_setzero_ps();
|
||||||
|
sum_r = _mm256_setzero_ps();
|
||||||
|
|
||||||
|
for (i = 0; i < taps; i += 8)
|
||||||
|
{
|
||||||
|
__m256 sinc;
|
||||||
|
__m256 buf_l = _mm256_loadu_ps(buffer_l + i);
|
||||||
|
__m256 buf_r = _mm256_loadu_ps(buffer_r + i);
|
||||||
|
|
||||||
|
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||||
|
{
|
||||||
|
__m256 deltas = _mm256_load_ps(delta_table + i);
|
||||||
|
sinc = _mm256_add_ps(_mm256_load_ps((const float*)phase_table + i),
|
||||||
|
_mm256_mul_ps(deltas, delta));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sinc = _mm256_load_ps((const float*)phase_table + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_l = _mm256_add_ps(sum_l, _mm256_mul_ps(buf_l, sinc));
|
||||||
|
sum_r = _mm256_add_ps(sum_r, _mm256_mul_ps(buf_r, sinc));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hadd on AVX is weird, and acts on low-lanes
|
||||||
|
* and high-lanes separately. */
|
||||||
|
__m256 res_l = _mm256_hadd_ps(sum_l, sum_l);
|
||||||
|
__m256 res_r = _mm256_hadd_ps(sum_r, sum_r);
|
||||||
|
res_l = _mm256_hadd_ps(res_l, res_l);
|
||||||
|
res_r = _mm256_hadd_ps(res_r, res_r);
|
||||||
|
res_l = _mm256_add_ps(_mm256_permute2f128_ps(res_l, res_l, 1), res_l);
|
||||||
|
res_r = _mm256_add_ps(_mm256_permute2f128_ps(res_r, res_r, 1), res_r);
|
||||||
|
|
||||||
|
/* This is optimized to mov %xmmN, [mem].
|
||||||
|
* There doesn't seem to be any _mm256_store_ss intrinsic. */
|
||||||
|
_mm_store_ss(output + 0, _mm256_extractf128_ps(res_l, 0));
|
||||||
|
_mm_store_ss(output + 1, _mm256_extractf128_ps(res_r, 0));
|
||||||
|
|
||||||
|
output += 2;
|
||||||
|
out_frames++;
|
||||||
|
resamp->time += ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data->output_frames = out_frames;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__SSE__)
|
||||||
|
static void resampler_sinc_process_sse(void *re_, struct resampler_data *data)
|
||||||
|
{
|
||||||
|
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
|
||||||
|
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
|
||||||
|
|
||||||
|
uint32_t ratio = phases / data->ratio;
|
||||||
|
const float *input = data->data_in;
|
||||||
|
float *output = data->data_out;
|
||||||
|
size_t frames = data->input_frames;
|
||||||
|
size_t out_frames = 0;
|
||||||
|
|
||||||
|
while (frames)
|
||||||
|
{
|
||||||
|
while (frames && resamp->time >= phases)
|
||||||
|
{
|
||||||
|
/* Push in reverse to make filter more obvious. */
|
||||||
|
if (!resamp->ptr)
|
||||||
|
resamp->ptr = resamp->taps;
|
||||||
|
resamp->ptr--;
|
||||||
|
|
||||||
|
resamp->buffer_l[resamp->ptr + resamp->taps] =
|
||||||
|
resamp->buffer_l[resamp->ptr] = *input++;
|
||||||
|
|
||||||
|
resamp->buffer_r[resamp->ptr + resamp->taps] =
|
||||||
|
resamp->buffer_r[resamp->ptr] = *input++;
|
||||||
|
|
||||||
|
resamp->time -= phases;
|
||||||
|
frames--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (resamp->time < phases)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
__m128 sum, sum_l, sum_r, delta;
|
||||||
|
float *phase_table = NULL;
|
||||||
|
float *delta_table = NULL;
|
||||||
|
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||||
|
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||||
|
unsigned taps = resamp->taps;
|
||||||
|
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||||
|
|
||||||
|
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||||
|
{
|
||||||
|
phase_table = resamp->phase_table + phase * taps * 2;
|
||||||
|
delta_table = phase_table + taps;
|
||||||
|
delta = _mm_set1_ps((float)
|
||||||
|
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
phase_table = resamp->phase_table + phase * taps;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_l = _mm_setzero_ps();
|
||||||
|
sum_r = _mm_setzero_ps();
|
||||||
|
|
||||||
|
for (i = 0; i < taps; i += 4)
|
||||||
|
{
|
||||||
|
__m128 deltas, _sinc;
|
||||||
|
__m128 buf_l = _mm_loadu_ps(buffer_l + i);
|
||||||
|
__m128 buf_r = _mm_loadu_ps(buffer_r + i);
|
||||||
|
|
||||||
|
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||||
|
{
|
||||||
|
deltas = _mm_load_ps(delta_table + i);
|
||||||
|
_sinc = _mm_add_ps(_mm_load_ps((const float*)phase_table + i),
|
||||||
|
_mm_mul_ps(deltas, delta));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_sinc = _mm_load_ps((const float*)phase_table + i);
|
||||||
|
}
|
||||||
|
sum_l = _mm_add_ps(sum_l, _mm_mul_ps(buf_l, _sinc));
|
||||||
|
sum_r = _mm_add_ps(sum_r, _mm_mul_ps(buf_r, _sinc));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Them annoying shuffles.
|
||||||
|
* sum_l = { l3, l2, l1, l0 }
|
||||||
|
* sum_r = { r3, r2, r1, r0 }
|
||||||
|
*/
|
||||||
|
|
||||||
|
sum = _mm_add_ps(_mm_shuffle_ps(sum_l, sum_r,
|
||||||
|
_MM_SHUFFLE(1, 0, 1, 0)),
|
||||||
|
_mm_shuffle_ps(sum_l, sum_r, _MM_SHUFFLE(3, 2, 3, 2)));
|
||||||
|
|
||||||
|
/* sum = { r1, r0, l1, l0 } + { r3, r2, l3, l2 }
|
||||||
|
* sum = { R1, R0, L1, L0 }
|
||||||
|
*/
|
||||||
|
|
||||||
|
sum = _mm_add_ps(_mm_shuffle_ps(sum, sum, _MM_SHUFFLE(3, 3, 1, 1)), sum);
|
||||||
|
|
||||||
|
/* sum = {R1, R1, L1, L1 } + { R1, R0, L1, L0 }
|
||||||
|
* sum = { X, R, X, L }
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Store L */
|
||||||
|
_mm_store_ss(output + 0, sum);
|
||||||
|
|
||||||
|
/* movehl { X, R, X, L } == { X, R, X, R } */
|
||||||
|
_mm_store_ss(output + 1, _mm_movehl_ps(sum, sum));
|
||||||
|
|
||||||
|
output += 2;
|
||||||
|
out_frames++;
|
||||||
|
resamp->time += ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data->output_frames = out_frames;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void resampler_sinc_process_c(void *re_, struct resampler_data *data)
|
||||||
|
{
|
||||||
|
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
|
||||||
|
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
|
||||||
|
|
||||||
|
uint32_t ratio = phases / data->ratio;
|
||||||
|
const float *input = data->data_in;
|
||||||
|
float *output = data->data_out;
|
||||||
|
size_t frames = data->input_frames;
|
||||||
|
size_t out_frames = 0;
|
||||||
|
|
||||||
|
while (frames)
|
||||||
|
{
|
||||||
|
while (frames && resamp->time >= phases)
|
||||||
|
{
|
||||||
|
/* Push in reverse to make filter more obvious. */
|
||||||
|
if (!resamp->ptr)
|
||||||
|
resamp->ptr = resamp->taps;
|
||||||
|
resamp->ptr--;
|
||||||
|
|
||||||
|
resamp->buffer_l[resamp->ptr + resamp->taps] =
|
||||||
|
resamp->buffer_l[resamp->ptr] = *input++;
|
||||||
|
|
||||||
|
resamp->buffer_r[resamp->ptr + resamp->taps] =
|
||||||
|
resamp->buffer_r[resamp->ptr] = *input++;
|
||||||
|
|
||||||
|
resamp->time -= phases;
|
||||||
|
frames--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (resamp->time < phases)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
float delta = 0.0f;
|
||||||
|
float sum_l = 0.0f;
|
||||||
|
float sum_r = 0.0f;
|
||||||
|
float *phase_table = NULL;
|
||||||
|
float *delta_table = NULL;
|
||||||
|
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||||
|
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||||
|
unsigned taps = resamp->taps;
|
||||||
|
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||||
|
|
||||||
|
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||||
|
{
|
||||||
|
phase_table = resamp->phase_table + phase * taps * 2;
|
||||||
|
delta_table = phase_table + taps;
|
||||||
|
delta = (float)
|
||||||
|
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
phase_table = resamp->phase_table + phase * taps;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < taps; i++)
|
||||||
|
{
|
||||||
|
float sinc_val = phase_table[i];
|
||||||
|
|
||||||
|
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||||
|
sinc_val = sinc_val + delta_table[i] * delta;
|
||||||
|
|
||||||
|
sum_l += buffer_l[i] * sinc_val;
|
||||||
|
sum_r += buffer_r[i] * sinc_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[0] = sum_l;
|
||||||
|
output[1] = sum_r;
|
||||||
|
|
||||||
|
output += 2;
|
||||||
|
out_frames++;
|
||||||
|
resamp->time += ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data->output_frames = out_frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void resampler_sinc_free(void *data)
|
||||||
|
{
|
||||||
|
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)data;
|
||||||
|
if (resamp)
|
||||||
|
memalign_free(resamp->main_buffer);
|
||||||
|
free(resamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sinc_init_table_kaiser(rarch_sinc_resampler_t *resamp,
|
||||||
|
double cutoff,
|
||||||
|
float *phase_table, int phases, int taps, bool calculate_delta)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
double window_mod = kaiser_window_function(0.0, resamp->kaiser_beta); /* Need to normalize w(0) to 1.0. */
|
||||||
|
int stride = calculate_delta ? 2 : 1;
|
||||||
|
double sidelobes = taps / 2.0;
|
||||||
|
|
||||||
|
for (i = 0; i < phases; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < taps; j++)
|
||||||
|
{
|
||||||
|
double sinc_phase;
|
||||||
|
float val;
|
||||||
|
int n = j * phases + i;
|
||||||
|
double window_phase = (double)n / (phases * taps); /* [0, 1). */
|
||||||
|
window_phase = 2.0 * window_phase - 1.0; /* [-1, 1) */
|
||||||
|
sinc_phase = sidelobes * window_phase;
|
||||||
|
val = cutoff * sinc(M_PI * sinc_phase * cutoff) *
|
||||||
|
kaiser_window_function(window_phase, resamp->kaiser_beta) / window_mod;
|
||||||
|
phase_table[i * stride * taps + j] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calculate_delta)
|
||||||
|
{
|
||||||
|
int phase;
|
||||||
|
int p;
|
||||||
|
|
||||||
|
for (p = 0; p < phases - 1; p++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < taps; j++)
|
||||||
|
{
|
||||||
|
float delta = phase_table[(p + 1) * stride * taps + j] -
|
||||||
|
phase_table[p * stride * taps + j];
|
||||||
|
phase_table[(p * stride + 1) * taps + j] = delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
phase = phases - 1;
|
||||||
|
for (j = 0; j < taps; j++)
|
||||||
|
{
|
||||||
|
float val, delta;
|
||||||
|
double sinc_phase;
|
||||||
|
int n = j * phases + (phase + 1);
|
||||||
|
double window_phase = (double)n / (phases * taps); /* (0, 1]. */
|
||||||
|
window_phase = 2.0 * window_phase - 1.0; /* (-1, 1] */
|
||||||
|
sinc_phase = sidelobes * window_phase;
|
||||||
|
|
||||||
|
val = cutoff * sinc(M_PI * sinc_phase * cutoff) *
|
||||||
|
kaiser_window_function(window_phase, resamp->kaiser_beta) / window_mod;
|
||||||
|
delta = (val - phase_table[phase * stride * taps + j]);
|
||||||
|
phase_table[(phase * stride + 1) * taps + j] = delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sinc_init_table_lanczos(rarch_sinc_resampler_t *resamp, double cutoff,
|
||||||
|
float *phase_table, int phases, int taps, bool calculate_delta)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
double window_mod = lanzcos_window_function(0.0); /* Need to normalize w(0) to 1.0. */
|
||||||
|
int stride = calculate_delta ? 2 : 1;
|
||||||
|
double sidelobes = taps / 2.0;
|
||||||
|
|
||||||
|
for (i = 0; i < phases; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < taps; j++)
|
||||||
|
{
|
||||||
|
double sinc_phase;
|
||||||
|
float val;
|
||||||
|
int n = j * phases + i;
|
||||||
|
double window_phase = (double)n / (phases * taps); /* [0, 1). */
|
||||||
|
window_phase = 2.0 * window_phase - 1.0; /* [-1, 1) */
|
||||||
|
sinc_phase = sidelobes * window_phase;
|
||||||
|
val = cutoff * sinc(M_PI * sinc_phase * cutoff) *
|
||||||
|
lanzcos_window_function(window_phase) / window_mod;
|
||||||
|
phase_table[i * stride * taps + j] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calculate_delta)
|
||||||
|
{
|
||||||
|
int phase;
|
||||||
|
int p;
|
||||||
|
|
||||||
|
for (p = 0; p < phases - 1; p++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < taps; j++)
|
||||||
|
{
|
||||||
|
float delta = phase_table[(p + 1) * stride * taps + j] -
|
||||||
|
phase_table[p * stride * taps + j];
|
||||||
|
phase_table[(p * stride + 1) * taps + j] = delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
phase = phases - 1;
|
||||||
|
for (j = 0; j < taps; j++)
|
||||||
|
{
|
||||||
|
float val, delta;
|
||||||
|
double sinc_phase;
|
||||||
|
int n = j * phases + (phase + 1);
|
||||||
|
double window_phase = (double)n / (phases * taps); /* (0, 1]. */
|
||||||
|
window_phase = 2.0 * window_phase - 1.0; /* (-1, 1] */
|
||||||
|
sinc_phase = sidelobes * window_phase;
|
||||||
|
|
||||||
|
val = cutoff * sinc(M_PI * sinc_phase * cutoff) *
|
||||||
|
lanzcos_window_function(window_phase) / window_mod;
|
||||||
|
delta = (val - phase_table[phase * stride * taps + j]);
|
||||||
|
phase_table[(phase * stride + 1) * taps + j] = delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *resampler_sinc_new(const struct resampler_config *config,
|
||||||
|
double bandwidth_mod, enum resampler_quality quality,
|
||||||
|
resampler_simd_mask_t mask)
|
||||||
|
{
|
||||||
|
double cutoff = 0.0;
|
||||||
|
size_t phase_elems = 0;
|
||||||
|
size_t elems = 0;
|
||||||
|
unsigned sidelobes = 0;
|
||||||
|
rarch_sinc_resampler_t *re = (rarch_sinc_resampler_t*)
|
||||||
|
calloc(1, sizeof(*re));
|
||||||
|
|
||||||
|
if (!re)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
re->window_type = SINC_WINDOW_NONE;
|
||||||
|
|
||||||
|
switch (quality)
|
||||||
|
{
|
||||||
|
case RESAMPLER_QUALITY_LOWEST:
|
||||||
|
cutoff = 0.98;
|
||||||
|
sidelobes = 2;
|
||||||
|
re->phase_bits = 12;
|
||||||
|
re->subphase_bits = 10;
|
||||||
|
re->window_type = SINC_WINDOW_LANCZOS;
|
||||||
|
re->enable_avx = 0;
|
||||||
|
break;
|
||||||
|
case RESAMPLER_QUALITY_LOWER:
|
||||||
|
cutoff = 0.98;
|
||||||
|
sidelobes = 4;
|
||||||
|
re->phase_bits = 12;
|
||||||
|
re->subphase_bits = 10;
|
||||||
|
re->window_type = SINC_WINDOW_LANCZOS;
|
||||||
|
re->enable_avx = 0;
|
||||||
|
break;
|
||||||
|
case RESAMPLER_QUALITY_HIGHER:
|
||||||
|
cutoff = 0.90;
|
||||||
|
sidelobes = 32;
|
||||||
|
re->phase_bits = 10;
|
||||||
|
re->subphase_bits = 14;
|
||||||
|
re->window_type = SINC_WINDOW_KAISER;
|
||||||
|
re->kaiser_beta = 10.5;
|
||||||
|
re->enable_avx = 1;
|
||||||
|
break;
|
||||||
|
case RESAMPLER_QUALITY_HIGHEST:
|
||||||
|
cutoff = 0.962;
|
||||||
|
sidelobes = 128;
|
||||||
|
re->phase_bits = 10;
|
||||||
|
re->subphase_bits = 14;
|
||||||
|
re->window_type = SINC_WINDOW_KAISER;
|
||||||
|
re->kaiser_beta = 14.5;
|
||||||
|
re->enable_avx = 1;
|
||||||
|
break;
|
||||||
|
case RESAMPLER_QUALITY_NORMAL:
|
||||||
|
case RESAMPLER_QUALITY_DONTCARE:
|
||||||
|
cutoff = 0.825;
|
||||||
|
sidelobes = 8;
|
||||||
|
re->phase_bits = 8;
|
||||||
|
re->subphase_bits = 16;
|
||||||
|
re->window_type = SINC_WINDOW_KAISER;
|
||||||
|
re->kaiser_beta = 5.5;
|
||||||
|
re->enable_avx = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
re->subphase_mask = (1 << re->subphase_bits) - 1;
|
||||||
|
re->subphase_mod = 1.0f / (1 << re->subphase_bits);
|
||||||
|
re->taps = sidelobes * 2;
|
||||||
|
|
||||||
|
/* Downsampling, must lower cutoff, and extend number of
|
||||||
|
* taps accordingly to keep same stopband attenuation. */
|
||||||
|
if (bandwidth_mod < 1.0)
|
||||||
|
{
|
||||||
|
cutoff *= bandwidth_mod;
|
||||||
|
re->taps = (unsigned)ceil(re->taps / bandwidth_mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Be SIMD-friendly. */
|
||||||
|
#if defined(__AVX__)
|
||||||
|
if (re->enable_avx)
|
||||||
|
re->taps = (re->taps + 7) & ~7;
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if defined(WANT_NEON)
|
||||||
|
re->taps = (re->taps + 7) & ~7;
|
||||||
|
#else
|
||||||
|
re->taps = (re->taps + 3) & ~3;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_elems = ((1 << re->phase_bits) * re->taps);
|
||||||
|
if (re->window_type == SINC_WINDOW_KAISER)
|
||||||
|
phase_elems = phase_elems * 2;
|
||||||
|
elems = phase_elems + 4 * re->taps;
|
||||||
|
|
||||||
|
re->main_buffer = (float*)memalign_alloc(128, sizeof(float) * elems);
|
||||||
|
if (!re->main_buffer)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
re->phase_table = re->main_buffer;
|
||||||
|
re->buffer_l = re->main_buffer + phase_elems;
|
||||||
|
re->buffer_r = re->buffer_l + 2 * re->taps;
|
||||||
|
|
||||||
|
switch (re->window_type)
|
||||||
|
{
|
||||||
|
case SINC_WINDOW_LANCZOS:
|
||||||
|
sinc_init_table_lanczos(re, cutoff, re->phase_table,
|
||||||
|
1 << re->phase_bits, re->taps, false);
|
||||||
|
break;
|
||||||
|
case SINC_WINDOW_KAISER:
|
||||||
|
sinc_init_table_kaiser(re, cutoff, re->phase_table,
|
||||||
|
1 << re->phase_bits, re->taps, true);
|
||||||
|
break;
|
||||||
|
case SINC_WINDOW_NONE:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
sinc_resampler.process = resampler_sinc_process_c;
|
||||||
|
|
||||||
|
if (mask & RESAMPLER_SIMD_AVX && re->enable_avx)
|
||||||
|
{
|
||||||
|
#if defined(__AVX__)
|
||||||
|
sinc_resampler.process = resampler_sinc_process_avx;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (mask & RESAMPLER_SIMD_SSE)
|
||||||
|
{
|
||||||
|
#if defined(__SSE__)
|
||||||
|
sinc_resampler.process = resampler_sinc_process_sse;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (mask & RESAMPLER_SIMD_NEON && re->window_type != SINC_WINDOW_KAISER)
|
||||||
|
{
|
||||||
|
#if defined(WANT_NEON)
|
||||||
|
sinc_resampler.process = resampler_sinc_process_neon;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return re;
|
||||||
|
|
||||||
|
error:
|
||||||
|
resampler_sinc_free(re);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
retro_resampler_t sinc_resampler = {
|
||||||
|
resampler_sinc_new,
|
||||||
|
resampler_sinc_process_c,
|
||||||
|
resampler_sinc_free,
|
||||||
|
RESAMPLER_API_VERSION,
|
||||||
|
"sinc",
|
||||||
|
"sinc"
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef WANT_NEON
|
|
@ -0,0 +1,74 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (sinc_resampler_neon.S).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__ARM_NEON__)
|
||||||
|
|
||||||
|
#ifndef __MACH__
|
||||||
|
.arm
|
||||||
|
#endif
|
||||||
|
.align 4
|
||||||
|
.globl process_sinc_neon_asm
|
||||||
|
#ifndef __MACH__
|
||||||
|
.type process_sinc_neon_asm, %function
|
||||||
|
#endif
|
||||||
|
.globl _process_sinc_neon_asm
|
||||||
|
#ifndef __MACH__
|
||||||
|
.type _process_sinc_neon_asm, %function
|
||||||
|
#endif
|
||||||
|
# void process_sinc_neon(float *out, const float *left, const float *right, const float *coeff, unsigned taps)
|
||||||
|
# Assumes taps is >= 8, and a multiple of 8.
|
||||||
|
process_sinc_neon_asm:
|
||||||
|
_process_sinc_neon_asm:
|
||||||
|
|
||||||
|
push {r4, lr}
|
||||||
|
vmov.f32 q0, #0.0
|
||||||
|
vmov.f32 q8, #0.0
|
||||||
|
|
||||||
|
# Taps argument (r4) goes on stack in armeabi.
|
||||||
|
ldr r4, [sp, #8]
|
||||||
|
|
||||||
|
1:
|
||||||
|
# Left
|
||||||
|
vld1.f32 {q2-q3}, [r1]!
|
||||||
|
# Right
|
||||||
|
vld1.f32 {q10-q11}, [r2]!
|
||||||
|
# Coeff
|
||||||
|
vld1.f32 {q12-q13}, [r3, :128]!
|
||||||
|
|
||||||
|
# Left / Right
|
||||||
|
vmla.f32 q0, q2, q12
|
||||||
|
vmla.f32 q8, q10, q12
|
||||||
|
vmla.f32 q0, q3, q13
|
||||||
|
vmla.f32 q8, q11, q13
|
||||||
|
|
||||||
|
subs r4, r4, #8
|
||||||
|
bne 1b
|
||||||
|
|
||||||
|
# Add everything together
|
||||||
|
vadd.f32 d0, d0, d1
|
||||||
|
vadd.f32 d16, d16, d17
|
||||||
|
vpadd.f32 d0, d0, d16
|
||||||
|
vst1.f32 d0, [r0]
|
||||||
|
|
||||||
|
pop {r4, pc}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,159 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (compat_fnmatch.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if __TEST_FNMATCH__
|
||||||
|
#include <assert.h>
|
||||||
|
#endif
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <compat/fnmatch.h>
|
||||||
|
|
||||||
|
/* Implemnentation of fnmatch(3) so it can be
|
||||||
|
* distributed to non *nix platforms.
|
||||||
|
*
|
||||||
|
* No flags are implemented ATM.
|
||||||
|
* We don't use them. Add flags as needed. */
|
||||||
|
|
||||||
|
int rl_fnmatch(const char *pattern, const char *string, int flags)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
const char *c = NULL;
|
||||||
|
int charmatch = 0;
|
||||||
|
|
||||||
|
for (c = pattern; *c != '\0'; c++)
|
||||||
|
{
|
||||||
|
/* String ended before pattern */
|
||||||
|
if ((*c != '*') && (*string == '\0'))
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
switch (*c)
|
||||||
|
{
|
||||||
|
/* Match any number of unknown chars */
|
||||||
|
case '*':
|
||||||
|
/* Find next node in the pattern
|
||||||
|
* ignoring multiple asterixes
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
c++;
|
||||||
|
if (*c == '\0')
|
||||||
|
return 0;
|
||||||
|
} while (*c == '*');
|
||||||
|
|
||||||
|
/* Match the remaining pattern
|
||||||
|
* ignoring more and more characters. */
|
||||||
|
do {
|
||||||
|
/* We reached the end of the string without a
|
||||||
|
* match. There is a way to optimize this by
|
||||||
|
* calculating the minimum chars needed to
|
||||||
|
* match the remaining pattern but I don't
|
||||||
|
* think it is worth the work ATM.
|
||||||
|
*/
|
||||||
|
if (*string == '\0')
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
rv = rl_fnmatch(c, string, flags);
|
||||||
|
string++;
|
||||||
|
} while (rv != 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
/* Match char from list */
|
||||||
|
case '[':
|
||||||
|
charmatch = 0;
|
||||||
|
for (c++; *c != ']'; c++)
|
||||||
|
{
|
||||||
|
/* Bad format */
|
||||||
|
if (*c == '\0')
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
/* Match already found */
|
||||||
|
if (charmatch)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (*c == *string)
|
||||||
|
charmatch = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No match in list */
|
||||||
|
if (!charmatch)
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
string++;
|
||||||
|
break;
|
||||||
|
/* Has any character */
|
||||||
|
case '?':
|
||||||
|
string++;
|
||||||
|
break;
|
||||||
|
/* Match following character verbatim */
|
||||||
|
case '\\':
|
||||||
|
c++;
|
||||||
|
/* Dangling escape at end of pattern.
|
||||||
|
* FIXME: Was c == '\0' (makes no sense).
|
||||||
|
* Not sure if c == NULL or *c == '\0'
|
||||||
|
* is intended. Assuming *c due to c++ right before. */
|
||||||
|
if (*c == '\0')
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
default:
|
||||||
|
if (*c != *string)
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End of string and end of pattend */
|
||||||
|
if (*string == '\0')
|
||||||
|
return 0;
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __TEST_FNMATCH__
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
assert(rl_fnmatch("TEST", "TEST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE?T", "TEST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE[Ssa]T", "TEST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE[Ssda]T", "TEsT", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE[Ssda]T", "TEdT", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE[Ssda]T", "TEaT", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TEST*", "TEST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TEST**", "TEST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE*ST*", "TEST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE**ST*", "TEST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE**ST*", "TExST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE**ST", "TEST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE**ST", "TExST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TE\\**ST", "TE*xST", 0) == 0);
|
||||||
|
assert(rl_fnmatch("*.*", "test.jpg", 0) == 0);
|
||||||
|
assert(rl_fnmatch("*.jpg", "test.jpg", 0) == 0);
|
||||||
|
assert(rl_fnmatch("*.[Jj][Pp][Gg]", "test.jPg", 0) == 0);
|
||||||
|
assert(rl_fnmatch("*.[Jj]*[Gg]", "test.jPg", 0) == 0);
|
||||||
|
assert(rl_fnmatch("TEST?", "TEST", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("TES[asd", "TEST", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("TEST\\", "TEST", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("TEST*S", "TEST", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("TE**ST", "TExT", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("TE\\*T", "TExT", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("TES?", "TES", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("TE", "TEST", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("TEST!", "TEST", 0) == FNM_NOMATCH);
|
||||||
|
assert(rl_fnmatch("DSAD", "TEST", 0) == FNM_NOMATCH);
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,219 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (compat_getopt.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <ctype.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <boolean.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
|
||||||
|
#include <compat/getopt.h>
|
||||||
|
#include <compat/strl.h>
|
||||||
|
#include <compat/strcasestr.h>
|
||||||
|
#include <compat/posix_string.h>
|
||||||
|
|
||||||
|
#include <retro_assert.h>
|
||||||
|
|
||||||
|
char *optarg;
|
||||||
|
int optind, opterr, optopt;
|
||||||
|
|
||||||
|
static bool is_short_option(const char *str)
|
||||||
|
{
|
||||||
|
return str[0] == '-' && str[1] != '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_long_option(const char *str)
|
||||||
|
{
|
||||||
|
return str[0] == '-' && str[1] == '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_short_index(char * const *argv)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
for (idx = 0; argv[idx]; idx++)
|
||||||
|
{
|
||||||
|
if (is_short_option(argv[idx]))
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_long_index(char * const *argv)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
for (idx = 0; argv[idx]; idx++)
|
||||||
|
{
|
||||||
|
if (is_long_option(argv[idx]))
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_short(const char *optstring, char * const *argv)
|
||||||
|
{
|
||||||
|
bool extra_opt, takes_arg, embedded_arg;
|
||||||
|
const char *opt = NULL;
|
||||||
|
char arg = argv[0][1];
|
||||||
|
|
||||||
|
if (arg == ':')
|
||||||
|
return '?';
|
||||||
|
|
||||||
|
opt = strchr(optstring, arg);
|
||||||
|
if (!opt)
|
||||||
|
return '?';
|
||||||
|
|
||||||
|
extra_opt = argv[0][2];
|
||||||
|
takes_arg = opt[1] == ':';
|
||||||
|
|
||||||
|
/* If we take an argument, and we see additional characters,
|
||||||
|
* this is in fact the argument (i.e. -cfoo is same as -c foo). */
|
||||||
|
embedded_arg = extra_opt && takes_arg;
|
||||||
|
|
||||||
|
if (takes_arg)
|
||||||
|
{
|
||||||
|
if (embedded_arg)
|
||||||
|
{
|
||||||
|
optarg = argv[0] + 2;
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
optarg = argv[1];
|
||||||
|
optind += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return optarg ? opt[0] : '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (embedded_arg)
|
||||||
|
{
|
||||||
|
/* If we see additional characters,
|
||||||
|
* and they don't take arguments, this
|
||||||
|
* means we have multiple flags in one. */
|
||||||
|
memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1);
|
||||||
|
return opt[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
optind++;
|
||||||
|
return opt[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_long(const struct option *longopts, char * const *argv)
|
||||||
|
{
|
||||||
|
size_t indice;
|
||||||
|
const struct option *opt = NULL;
|
||||||
|
for (indice = 0; longopts[indice].name; indice++)
|
||||||
|
{
|
||||||
|
if (!strcmp(longopts[indice].name, &argv[0][2]))
|
||||||
|
{
|
||||||
|
opt = &longopts[indice];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opt)
|
||||||
|
return '?';
|
||||||
|
|
||||||
|
/* getopt_long has an "optional" arg, but we don't bother with that. */
|
||||||
|
if (opt->has_arg && !argv[1])
|
||||||
|
return '?';
|
||||||
|
|
||||||
|
if (opt->has_arg)
|
||||||
|
{
|
||||||
|
optarg = argv[1];
|
||||||
|
optind += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
optind++;
|
||||||
|
|
||||||
|
if (opt->flag)
|
||||||
|
{
|
||||||
|
*opt->flag = opt->val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return opt->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shuffle_block(char **begin, char **last, char **end)
|
||||||
|
{
|
||||||
|
ptrdiff_t len = last - begin;
|
||||||
|
const char **tmp = (const char**)calloc(len, sizeof(const char*));
|
||||||
|
|
||||||
|
retro_assert(tmp);
|
||||||
|
|
||||||
|
memcpy((void*)tmp, begin, len * sizeof(const char*));
|
||||||
|
memmove(begin, last, (end - last) * sizeof(const char*));
|
||||||
|
memcpy(end - len, tmp, len * sizeof(const char*));
|
||||||
|
|
||||||
|
free((void*)tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getopt_long(int argc, char *argv[],
|
||||||
|
const char *optstring, const struct option *longopts, int *longindex)
|
||||||
|
{
|
||||||
|
int short_index, long_index;
|
||||||
|
|
||||||
|
(void)longindex;
|
||||||
|
|
||||||
|
if (optind == 0)
|
||||||
|
optind = 1;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
short_index = find_short_index(&argv[optind]);
|
||||||
|
long_index = find_long_index(&argv[optind]);
|
||||||
|
|
||||||
|
/* We're done here. */
|
||||||
|
if (short_index == -1 && long_index == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Reorder argv so that non-options come last.
|
||||||
|
* Non-POSIXy, but that's what getopt does by default. */
|
||||||
|
if ((short_index > 0) && ((short_index < long_index) || (long_index == -1)))
|
||||||
|
{
|
||||||
|
shuffle_block(&argv[optind], &argv[optind + short_index], &argv[argc]);
|
||||||
|
short_index = 0;
|
||||||
|
}
|
||||||
|
else if ((long_index > 0) && ((long_index < short_index)
|
||||||
|
|| (short_index == -1)))
|
||||||
|
{
|
||||||
|
shuffle_block(&argv[optind], &argv[optind + long_index], &argv[argc]);
|
||||||
|
long_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
retro_assert(short_index == 0 || long_index == 0);
|
||||||
|
|
||||||
|
if (short_index == 0)
|
||||||
|
return parse_short(optstring, &argv[optind]);
|
||||||
|
if (long_index == 0)
|
||||||
|
return parse_long(longopts, &argv[optind]);
|
||||||
|
|
||||||
|
return '?';
|
||||||
|
}
|
|
@ -0,0 +1,619 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013, Kenneth MacKay
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <compat/ifaddrs.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netpacket/packet.h>
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
|
||||||
|
typedef struct NetlinkList
|
||||||
|
{
|
||||||
|
struct NetlinkList *m_next;
|
||||||
|
struct nlmsghdr *m_data;
|
||||||
|
unsigned int m_size;
|
||||||
|
} NetlinkList;
|
||||||
|
|
||||||
|
static int netlink_socket(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_nl l_addr;
|
||||||
|
int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||||
|
|
||||||
|
if(l_socket < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(&l_addr, 0, sizeof(l_addr));
|
||||||
|
l_addr.nl_family = AF_NETLINK;
|
||||||
|
if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
|
||||||
|
{
|
||||||
|
close(l_socket);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return l_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int netlink_send(int p_socket, int p_request)
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
struct nlmsghdr m_hdr;
|
||||||
|
struct rtgenmsg m_msg;
|
||||||
|
} l_data;
|
||||||
|
struct sockaddr_nl l_addr;
|
||||||
|
|
||||||
|
memset(&l_data, 0, sizeof(l_data));
|
||||||
|
|
||||||
|
l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
|
||||||
|
l_data.m_hdr.nlmsg_type = p_request;
|
||||||
|
l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
|
||||||
|
l_data.m_hdr.nlmsg_pid = 0;
|
||||||
|
l_data.m_hdr.nlmsg_seq = p_socket;
|
||||||
|
l_data.m_msg.rtgen_family = AF_UNSPEC;
|
||||||
|
|
||||||
|
memset(&l_addr, 0, sizeof(l_addr));
|
||||||
|
l_addr.nl_family = AF_NETLINK;
|
||||||
|
return (sendto(p_socket, &l_data.m_hdr, l_data.m_hdr.nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
|
||||||
|
{
|
||||||
|
struct msghdr l_msg;
|
||||||
|
struct iovec l_iov = { p_buffer, p_len };
|
||||||
|
struct sockaddr_nl l_addr;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
l_msg.msg_name = (void *)&l_addr;
|
||||||
|
l_msg.msg_namelen = sizeof(l_addr);
|
||||||
|
l_msg.msg_iov = &l_iov;
|
||||||
|
l_msg.msg_iovlen = 1;
|
||||||
|
l_msg.msg_control = NULL;
|
||||||
|
l_msg.msg_controllen = 0;
|
||||||
|
l_msg.msg_flags = 0;
|
||||||
|
int l_result = recvmsg(p_socket, &l_msg, 0);
|
||||||
|
|
||||||
|
if(l_result < 0)
|
||||||
|
{
|
||||||
|
if(errno == EINTR)
|
||||||
|
continue;
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l_msg.msg_flags & MSG_TRUNC) /* buffer too small */
|
||||||
|
return -1;
|
||||||
|
return l_result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done)
|
||||||
|
{
|
||||||
|
size_t l_size = 4096;
|
||||||
|
void *l_buffer = NULL;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
free(l_buffer);
|
||||||
|
l_buffer = malloc(l_size);
|
||||||
|
if (l_buffer == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int l_read = netlink_recv(p_socket, l_buffer, l_size);
|
||||||
|
*p_size = l_read;
|
||||||
|
|
||||||
|
if(l_read == -2)
|
||||||
|
{
|
||||||
|
free(l_buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l_read >= 0)
|
||||||
|
{
|
||||||
|
pid_t l_pid = getpid();
|
||||||
|
struct nlmsghdr *l_hdr;
|
||||||
|
for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
|
||||||
|
{
|
||||||
|
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||||
|
{
|
||||||
|
*p_done = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l_hdr->nlmsg_type == NLMSG_ERROR)
|
||||||
|
{
|
||||||
|
free(l_buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
l_size *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
|
||||||
|
{
|
||||||
|
NetlinkList *l_item = malloc(sizeof(NetlinkList));
|
||||||
|
if (l_item == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
l_item->m_next = NULL;
|
||||||
|
l_item->m_data = p_data;
|
||||||
|
l_item->m_size = p_size;
|
||||||
|
return l_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeResultList(NetlinkList *p_list)
|
||||||
|
{
|
||||||
|
NetlinkList *l_cur;
|
||||||
|
while(p_list)
|
||||||
|
{
|
||||||
|
l_cur = p_list;
|
||||||
|
p_list = p_list->m_next;
|
||||||
|
free(l_cur->m_data);
|
||||||
|
free(l_cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NetlinkList *getResultList(int p_socket, int p_request)
|
||||||
|
{
|
||||||
|
if(netlink_send(p_socket, p_request) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
NetlinkList *l_list = NULL;
|
||||||
|
NetlinkList *l_end = NULL;
|
||||||
|
int l_size;
|
||||||
|
int l_done = 0;
|
||||||
|
while(!l_done)
|
||||||
|
{
|
||||||
|
NetlinkList *l_item = NULL;
|
||||||
|
struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
|
||||||
|
if(!l_hdr)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
l_item = newListItem(l_hdr, l_size);
|
||||||
|
if (!l_item)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if(!l_list)
|
||||||
|
l_list = l_item;
|
||||||
|
else
|
||||||
|
l_end->m_next = l_item;
|
||||||
|
l_end = l_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return l_list;
|
||||||
|
|
||||||
|
error:
|
||||||
|
freeResultList(l_list);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t maxSize(size_t a, size_t b)
|
||||||
|
{
|
||||||
|
return (a > b ? a : b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
|
||||||
|
{
|
||||||
|
switch(p_family)
|
||||||
|
{
|
||||||
|
case AF_INET:
|
||||||
|
return sizeof(struct sockaddr_in);
|
||||||
|
case AF_INET6:
|
||||||
|
return sizeof(struct sockaddr_in6);
|
||||||
|
case AF_PACKET:
|
||||||
|
return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
|
||||||
|
{
|
||||||
|
switch(p_family)
|
||||||
|
{
|
||||||
|
case AF_INET:
|
||||||
|
memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
|
||||||
|
break;
|
||||||
|
case AF_PACKET:
|
||||||
|
memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
|
||||||
|
((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
memcpy(p_dest->sa_data, p_data, p_size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p_dest->sa_family = p_family;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
|
||||||
|
{
|
||||||
|
if(!*p_resultList)
|
||||||
|
*p_resultList = p_entry;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct ifaddrs *l_cur = *p_resultList;
|
||||||
|
while(l_cur->ifa_next)
|
||||||
|
l_cur = l_cur->ifa_next;
|
||||||
|
l_cur->ifa_next = p_entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
|
||||||
|
{
|
||||||
|
struct ifaddrs *l_entry = NULL;
|
||||||
|
struct rtattr *l_rta = NULL;
|
||||||
|
struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
|
||||||
|
size_t l_nameSize = 0;
|
||||||
|
size_t l_addrSize = 0;
|
||||||
|
size_t l_dataSize = 0;
|
||||||
|
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
|
||||||
|
|
||||||
|
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
|
||||||
|
{
|
||||||
|
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||||
|
switch(l_rta->rta_type)
|
||||||
|
{
|
||||||
|
case IFLA_ADDRESS:
|
||||||
|
case IFLA_BROADCAST:
|
||||||
|
l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
|
||||||
|
break;
|
||||||
|
case IFLA_IFNAME:
|
||||||
|
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
|
||||||
|
break;
|
||||||
|
case IFLA_STATS:
|
||||||
|
l_dataSize += NLMSG_ALIGN(l_rtaSize);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l_entry = malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
|
||||||
|
if (l_entry == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(l_entry, 0, sizeof(struct ifaddrs));
|
||||||
|
l_entry->ifa_name = "";
|
||||||
|
|
||||||
|
char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
|
||||||
|
char *l_name = l_index + sizeof(int);
|
||||||
|
char *l_addr = l_name + l_nameSize;
|
||||||
|
char *l_data = l_addr + l_addrSize;
|
||||||
|
|
||||||
|
// save the interface index so we can look it up when handling the addresses.
|
||||||
|
memcpy(l_index, &l_info->ifi_index, sizeof(int));
|
||||||
|
|
||||||
|
l_entry->ifa_flags = l_info->ifi_flags;
|
||||||
|
|
||||||
|
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
|
||||||
|
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
|
||||||
|
{
|
||||||
|
void *l_rtaData = RTA_DATA(l_rta);
|
||||||
|
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||||
|
switch(l_rta->rta_type)
|
||||||
|
{
|
||||||
|
case IFLA_ADDRESS:
|
||||||
|
case IFLA_BROADCAST:
|
||||||
|
{
|
||||||
|
size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
|
||||||
|
makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
|
||||||
|
((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
|
||||||
|
((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
|
||||||
|
if(l_rta->rta_type == IFLA_ADDRESS)
|
||||||
|
l_entry->ifa_addr = (struct sockaddr *)l_addr;
|
||||||
|
else
|
||||||
|
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
|
||||||
|
l_addr += NLMSG_ALIGN(l_addrLen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IFLA_IFNAME:
|
||||||
|
strncpy(l_name, l_rtaData, l_rtaDataSize);
|
||||||
|
l_name[l_rtaDataSize] = '\0';
|
||||||
|
l_entry->ifa_name = l_name;
|
||||||
|
break;
|
||||||
|
case IFLA_STATS:
|
||||||
|
memcpy(l_data, l_rtaData, l_rtaDataSize);
|
||||||
|
l_entry->ifa_data = l_data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addToEnd(p_resultList, l_entry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks)
|
||||||
|
{
|
||||||
|
int l_num = 0;
|
||||||
|
struct ifaddrs *l_cur = *p_links;
|
||||||
|
while(l_cur && l_num < p_numLinks)
|
||||||
|
{
|
||||||
|
int l_index;
|
||||||
|
char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
|
||||||
|
|
||||||
|
memcpy(&l_index, l_indexPtr, sizeof(int));
|
||||||
|
if(l_index == p_index)
|
||||||
|
return l_cur;
|
||||||
|
|
||||||
|
l_cur = l_cur->ifa_next;
|
||||||
|
++l_num;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks)
|
||||||
|
{
|
||||||
|
size_t l_nameSize = 0;
|
||||||
|
size_t l_addrSize = 0;
|
||||||
|
int l_addedNetmask = 0;
|
||||||
|
struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
|
||||||
|
struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
|
||||||
|
|
||||||
|
if(l_info->ifa_family == AF_PACKET)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
|
||||||
|
struct rtattr *l_rta;
|
||||||
|
|
||||||
|
for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
|
||||||
|
{
|
||||||
|
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||||
|
|
||||||
|
switch(l_rta->rta_type)
|
||||||
|
{
|
||||||
|
case IFA_ADDRESS:
|
||||||
|
case IFA_LOCAL:
|
||||||
|
if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
|
||||||
|
{
|
||||||
|
/* make room for netmask */
|
||||||
|
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
|
||||||
|
l_addedNetmask = 1;
|
||||||
|
}
|
||||||
|
case IFA_BROADCAST:
|
||||||
|
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
|
||||||
|
break;
|
||||||
|
case IFA_LABEL:
|
||||||
|
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
|
||||||
|
if (l_entry == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(l_entry, 0, sizeof(struct ifaddrs));
|
||||||
|
l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
|
||||||
|
|
||||||
|
char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
|
||||||
|
char *l_addr = l_name + l_nameSize;
|
||||||
|
|
||||||
|
l_entry->ifa_flags = l_info->ifa_flags;
|
||||||
|
if(l_interface)
|
||||||
|
l_entry->ifa_flags |= l_interface->ifa_flags;
|
||||||
|
|
||||||
|
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
|
||||||
|
for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
|
||||||
|
{
|
||||||
|
void *l_rtaData = RTA_DATA(l_rta);
|
||||||
|
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||||
|
switch(l_rta->rta_type)
|
||||||
|
{
|
||||||
|
case IFA_ADDRESS:
|
||||||
|
case IFA_BROADCAST:
|
||||||
|
case IFA_LOCAL:
|
||||||
|
{
|
||||||
|
size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
|
||||||
|
makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
|
||||||
|
if(l_info->ifa_family == AF_INET6)
|
||||||
|
{
|
||||||
|
if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
|
||||||
|
((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l_rta->rta_type == IFA_ADDRESS)
|
||||||
|
{
|
||||||
|
/* apparently in a point-to-point network IFA_ADDRESS
|
||||||
|
* contains the dest address and IFA_LOCAL contains the local address */
|
||||||
|
if(l_entry->ifa_addr)
|
||||||
|
l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
|
||||||
|
else
|
||||||
|
l_entry->ifa_addr = (struct sockaddr *)l_addr;
|
||||||
|
}
|
||||||
|
else if(l_rta->rta_type == IFA_LOCAL)
|
||||||
|
{
|
||||||
|
if(l_entry->ifa_addr)
|
||||||
|
l_entry->ifa_dstaddr = l_entry->ifa_addr;
|
||||||
|
l_entry->ifa_addr = (struct sockaddr *)l_addr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
|
||||||
|
l_addr += NLMSG_ALIGN(l_addrLen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IFA_LABEL:
|
||||||
|
strncpy(l_name, l_rtaData, l_rtaDataSize);
|
||||||
|
l_name[l_rtaDataSize] = '\0';
|
||||||
|
l_entry->ifa_name = l_name;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l_entry->ifa_addr &&
|
||||||
|
( l_entry->ifa_addr->sa_family == AF_INET
|
||||||
|
|| l_entry->ifa_addr->sa_family == AF_INET6))
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
char l_mask[16];
|
||||||
|
unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET
|
||||||
|
? 32 : 128);
|
||||||
|
unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix
|
||||||
|
? l_maxPrefix : l_info->ifa_prefixlen);
|
||||||
|
|
||||||
|
l_mask[0] = '\0';
|
||||||
|
|
||||||
|
for(i=0; i<(l_prefix/8); ++i)
|
||||||
|
l_mask[i] = 0xff;
|
||||||
|
if(l_prefix % 8)
|
||||||
|
l_mask[i] = 0xff << (8 - (l_prefix % 8));
|
||||||
|
|
||||||
|
makeSockaddr(l_entry->ifa_addr->sa_family,
|
||||||
|
(struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
|
||||||
|
l_entry->ifa_netmask = (struct sockaddr *)l_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
addToEnd(p_resultList, l_entry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int interpretLinks(int p_socket, NetlinkList *p_netlinkList,
|
||||||
|
struct ifaddrs **p_resultList)
|
||||||
|
{
|
||||||
|
int l_numLinks = 0;
|
||||||
|
pid_t l_pid = getpid();
|
||||||
|
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *l_hdr = NULL;
|
||||||
|
unsigned int l_nlsize = p_netlinkList->m_size;
|
||||||
|
|
||||||
|
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
|
||||||
|
{
|
||||||
|
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(l_hdr->nlmsg_type == RTM_NEWLINK)
|
||||||
|
{
|
||||||
|
if(interpretLink(l_hdr, p_resultList) == -1)
|
||||||
|
return -1;
|
||||||
|
++l_numLinks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l_numLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList,
|
||||||
|
struct ifaddrs **p_resultList, int p_numLinks)
|
||||||
|
{
|
||||||
|
pid_t l_pid = getpid();
|
||||||
|
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *l_hdr = NULL;
|
||||||
|
unsigned int l_nlsize = p_netlinkList->m_size;
|
||||||
|
|
||||||
|
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
|
||||||
|
{
|
||||||
|
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(l_hdr->nlmsg_type == RTM_NEWADDR)
|
||||||
|
{
|
||||||
|
if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getifaddrs(struct ifaddrs **ifap)
|
||||||
|
{
|
||||||
|
int l_socket = 0;
|
||||||
|
int l_result = 0;
|
||||||
|
if(!ifap)
|
||||||
|
return -1;
|
||||||
|
*ifap = NULL;
|
||||||
|
|
||||||
|
l_socket = netlink_socket();
|
||||||
|
if(l_socket < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK);
|
||||||
|
if(!l_linkResults)
|
||||||
|
{
|
||||||
|
close(l_socket);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetlinkList *l_addrResults = getResultList(l_socket, RTM_GETADDR);
|
||||||
|
if(!l_addrResults)
|
||||||
|
{
|
||||||
|
close(l_socket);
|
||||||
|
freeResultList(l_linkResults);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
|
||||||
|
|
||||||
|
if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
|
||||||
|
l_result = -1;
|
||||||
|
|
||||||
|
freeResultList(l_linkResults);
|
||||||
|
freeResultList(l_addrResults);
|
||||||
|
close(l_socket);
|
||||||
|
return l_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeifaddrs(struct ifaddrs *ifa)
|
||||||
|
{
|
||||||
|
struct ifaddrs *l_cur = NULL;
|
||||||
|
|
||||||
|
while(ifa)
|
||||||
|
{
|
||||||
|
l_cur = ifa;
|
||||||
|
ifa = ifa->ifa_next;
|
||||||
|
free(l_cur);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (compat_posix_string.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <ctype.h>
|
||||||
|
|
||||||
|
#include <compat/posix_string.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#undef strcasecmp
|
||||||
|
#undef strdup
|
||||||
|
#undef isblank
|
||||||
|
#undef strtok_r
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <compat/strl.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int retro_strcasecmp__(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
while (*a && *b)
|
||||||
|
{
|
||||||
|
int a_ = tolower(*a);
|
||||||
|
int b_ = tolower(*b);
|
||||||
|
|
||||||
|
if (a_ != b_)
|
||||||
|
return a_ - b_;
|
||||||
|
|
||||||
|
a++;
|
||||||
|
b++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tolower(*a) - tolower(*b);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *retro_strdup__(const char *orig)
|
||||||
|
{
|
||||||
|
size_t len = strlen(orig) + 1;
|
||||||
|
char *ret = (char*)malloc(len);
|
||||||
|
if (!ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
strlcpy(ret, orig, len);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int retro_isblank__(int c)
|
||||||
|
{
|
||||||
|
return (c == ' ') || (c == '\t');
|
||||||
|
}
|
||||||
|
|
||||||
|
char *retro_strtok_r__(char *str, const char *delim, char **saveptr)
|
||||||
|
{
|
||||||
|
char *first = NULL;
|
||||||
|
if (!saveptr || !delim)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (str)
|
||||||
|
*saveptr = str;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
char *ptr = NULL;
|
||||||
|
first = *saveptr;
|
||||||
|
while (*first && strchr(delim, *first))
|
||||||
|
*first++ = '\0';
|
||||||
|
|
||||||
|
if (*first == '\0')
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ptr = first + 1;
|
||||||
|
|
||||||
|
while (*ptr && !strchr(delim, *ptr))
|
||||||
|
ptr++;
|
||||||
|
|
||||||
|
*saveptr = ptr + (*ptr ? 1 : 0);
|
||||||
|
*ptr = '\0';
|
||||||
|
} while (strlen(first) == 0);
|
||||||
|
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (compat_snprintf.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
#include <retro_common.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#if _MSC_VER < 1800
|
||||||
|
#define va_copy(dst, src) ((dst) = (src))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if _MSC_VER < 1300
|
||||||
|
#define _vscprintf c89_vscprintf_retro__
|
||||||
|
|
||||||
|
static int c89_vscprintf_retro__(const char *format, va_list pargs)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
va_list argcopy;
|
||||||
|
va_copy(argcopy, pargs);
|
||||||
|
retval = vsnprintf(NULL, 0, format, argcopy);
|
||||||
|
va_end(argcopy);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */
|
||||||
|
|
||||||
|
int c99_vsnprintf_retro__(char *outBuf, size_t size, const char *format, va_list ap)
|
||||||
|
{
|
||||||
|
int count = -1;
|
||||||
|
|
||||||
|
if (size != 0)
|
||||||
|
#if (_MSC_VER <= 1310)
|
||||||
|
count = _vsnprintf(outBuf, size, format, ap);
|
||||||
|
#else
|
||||||
|
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
|
||||||
|
#endif
|
||||||
|
if (count == -1)
|
||||||
|
count = _vscprintf(format, ap);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
count = c99_vsnprintf_retro__(outBuf, size, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (compat_strcasestr.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <ctype.h>
|
||||||
|
|
||||||
|
#include <compat/strcasestr.h>
|
||||||
|
|
||||||
|
/* Pretty much strncasecmp. */
|
||||||
|
static int casencmp(const char *a, const char *b, size_t n)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
int a_lower = tolower(a[i]);
|
||||||
|
int b_lower = tolower(b[i]);
|
||||||
|
if (a_lower != b_lower)
|
||||||
|
return a_lower - b_lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strcasestr_retro__(const char *haystack, const char *needle)
|
||||||
|
{
|
||||||
|
size_t i, search_off;
|
||||||
|
size_t hay_len = strlen(haystack);
|
||||||
|
size_t needle_len = strlen(needle);
|
||||||
|
|
||||||
|
if (needle_len > hay_len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
search_off = hay_len - needle_len;
|
||||||
|
for (i = 0; i <= search_off; i++)
|
||||||
|
if (!casencmp(haystack + i, needle, needle_len))
|
||||||
|
return (char*)haystack + i;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (compat_strl.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <compat/strl.h>
|
||||||
|
|
||||||
|
/* Implementation of strlcpy()/strlcat() based on OpenBSD. */
|
||||||
|
|
||||||
|
#ifndef __MACH__
|
||||||
|
|
||||||
|
size_t strlcpy(char *dest, const char *source, size_t size)
|
||||||
|
{
|
||||||
|
size_t src_size = 0;
|
||||||
|
size_t n = size;
|
||||||
|
|
||||||
|
if (n)
|
||||||
|
while (--n && (*dest++ = *source++)) src_size++;
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
{
|
||||||
|
if (size) *dest = '\0';
|
||||||
|
while (*source++) src_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return src_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlcat(char *dest, const char *source, size_t size)
|
||||||
|
{
|
||||||
|
size_t len = strlen(dest);
|
||||||
|
|
||||||
|
dest += len;
|
||||||
|
|
||||||
|
if (len > size)
|
||||||
|
size = 0;
|
||||||
|
else
|
||||||
|
size -= len;
|
||||||
|
|
||||||
|
return len + strlcpy(dest, source, size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *strldup(const char *s, size_t n)
|
||||||
|
{
|
||||||
|
char *dst = (char*)malloc(sizeof(char) * (n + 1));
|
||||||
|
strlcpy(dst, s, n);
|
||||||
|
return dst;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (compat_snprintf.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
#include <retro_common.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||||
|
#define va_copy(dst, src) ((dst) = (src))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int c89_vscprintf_retro__(const char *format, va_list pargs)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
va_list argcopy;
|
||||||
|
va_copy(argcopy, pargs);
|
||||||
|
retval = vsnprintf(NULL, 0, format, argcopy);
|
||||||
|
va_end(argcopy);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include <compat/fopen_utf8.h>
|
||||||
|
#include <encodings/utf.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||||
|
#ifndef LEGACY_WIN32
|
||||||
|
#define LEGACY_WIN32
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#undef fopen
|
||||||
|
|
||||||
|
FILE* fopen_utf8(const char * filename, const char * mode)
|
||||||
|
{
|
||||||
|
#if defined(_XBOX)
|
||||||
|
return fopen(filename, mode);
|
||||||
|
#elif defined(LEGACY_WIN32)
|
||||||
|
FILE *ret = NULL;
|
||||||
|
char * filename_local = utf8_to_local_string_alloc(filename);
|
||||||
|
|
||||||
|
if (!filename_local)
|
||||||
|
return NULL;
|
||||||
|
ret = fopen(filename_local, mode);
|
||||||
|
if (filename_local)
|
||||||
|
free(filename_local);
|
||||||
|
return ret;
|
||||||
|
#else
|
||||||
|
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
|
||||||
|
wchar_t * mode_w = utf8_to_utf16_string_alloc(mode);
|
||||||
|
FILE* ret = _wfopen(filename_w, mode_w);
|
||||||
|
free(filename_w);
|
||||||
|
free(mode_w);
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef __LIBRETRO_SDK_CRT_STRING_H_
|
||||||
|
#define __LIBRETRO_SDK_CRT_STRING_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void *memcpy(void *dst, const void *src, size_t len);
|
||||||
|
|
||||||
|
void *memset(void *b, int c, size_t len);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <cruntime.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void *memset(void *dst, int val, size_t count)
|
||||||
|
{
|
||||||
|
void *start = dst;
|
||||||
|
|
||||||
|
#if defined(_M_IA64) || defined (_M_AMD64) || defined(_M_ALPHA) || defined (_M_PPC)
|
||||||
|
extern void RtlFillMemory(void *, size_t count, char);
|
||||||
|
|
||||||
|
RtlFillMemory(dst, count, (char)val);
|
||||||
|
#else
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
*(char*)dst = (char)val;
|
||||||
|
dst = (char*)dst + 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memcpy(void *dst, const void *src, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
((unsigned char *)dst)[i] = ((unsigned char *)src)[i];
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (dylib.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dynamic/dylib.h>
|
||||||
|
#include <encodings/utf.h>
|
||||||
|
|
||||||
|
#ifdef NEED_DYNAMIC
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <compat/posix_string.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Assume W-functions do not work below Win2K and Xbox platforms */
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||||
|
|
||||||
|
#ifndef LEGACY_WIN32
|
||||||
|
#define LEGACY_WIN32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static char last_dyn_error[512];
|
||||||
|
|
||||||
|
static void set_dl_error(void)
|
||||||
|
{
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
|
||||||
|
if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
||||||
|
NULL,
|
||||||
|
err,
|
||||||
|
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
|
||||||
|
last_dyn_error,
|
||||||
|
sizeof(last_dyn_error) - 1,
|
||||||
|
NULL) == 0)
|
||||||
|
snprintf(last_dyn_error, sizeof(last_dyn_error) - 1,
|
||||||
|
"unknown error %lu", err);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dylib_load:
|
||||||
|
* @path : Path to libretro core library.
|
||||||
|
*
|
||||||
|
* Platform independent dylib loading.
|
||||||
|
*
|
||||||
|
* Returns: library handle on success, otherwise NULL.
|
||||||
|
**/
|
||||||
|
dylib_t dylib_load(const char *path)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
int prevmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
||||||
|
#ifdef LEGACY_WIN32
|
||||||
|
dylib_t lib = LoadLibrary(path);
|
||||||
|
#else
|
||||||
|
wchar_t *pathW = utf8_to_utf16_string_alloc(path);
|
||||||
|
dylib_t lib = LoadLibraryW(pathW);
|
||||||
|
|
||||||
|
free(pathW);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SetErrorMode(prevmode);
|
||||||
|
|
||||||
|
if (!lib)
|
||||||
|
{
|
||||||
|
set_dl_error();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
last_dyn_error[0] = 0;
|
||||||
|
#else
|
||||||
|
dylib_t lib = dlopen(path, RTLD_LAZY);
|
||||||
|
#endif
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dylib_error(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (last_dyn_error[0])
|
||||||
|
return last_dyn_error;
|
||||||
|
return NULL;
|
||||||
|
#else
|
||||||
|
return (char*)dlerror();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
function_t dylib_proc(dylib_t lib, const char *proc)
|
||||||
|
{
|
||||||
|
function_t sym;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
sym = (function_t)GetProcAddress(lib ?
|
||||||
|
(HMODULE)lib : GetModuleHandle(NULL), proc);
|
||||||
|
if (!sym)
|
||||||
|
{
|
||||||
|
set_dl_error();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
last_dyn_error[0] = 0;
|
||||||
|
#else
|
||||||
|
void *ptr_sym = NULL;
|
||||||
|
|
||||||
|
if (lib)
|
||||||
|
ptr_sym = dlsym(lib, proc);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
void *handle = dlopen(NULL, RTLD_LAZY);
|
||||||
|
if (handle)
|
||||||
|
{
|
||||||
|
ptr_sym = dlsym(handle, proc);
|
||||||
|
dlclose(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dirty hack to workaround the non-legality of
|
||||||
|
* (void*) -> fn-pointer casts. */
|
||||||
|
memcpy(&sym, &ptr_sym, sizeof(void*));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dylib_close:
|
||||||
|
* @lib : Library handle.
|
||||||
|
*
|
||||||
|
* Frees library handle.
|
||||||
|
**/
|
||||||
|
void dylib_close(dylib_t lib)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!FreeLibrary((HMODULE)lib))
|
||||||
|
set_dl_error();
|
||||||
|
last_dyn_error[0] = 0;
|
||||||
|
#else
|
||||||
|
#ifndef NO_DLCLOSE
|
||||||
|
dlclose(lib);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,90 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (encoding_crc32.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <encodings/crc32.h>
|
||||||
|
|
||||||
|
static const uint32_t crc32_table[256] = {
|
||||||
|
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
|
||||||
|
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
|
||||||
|
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
|
||||||
|
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
|
||||||
|
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
|
||||||
|
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
|
||||||
|
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
|
||||||
|
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
|
||||||
|
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
|
||||||
|
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
|
||||||
|
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
|
||||||
|
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
|
||||||
|
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
|
||||||
|
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
|
||||||
|
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
|
||||||
|
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
|
||||||
|
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
|
||||||
|
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
|
||||||
|
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
|
||||||
|
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
|
||||||
|
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
|
||||||
|
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
|
||||||
|
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
|
||||||
|
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
|
||||||
|
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
|
||||||
|
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
|
||||||
|
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
|
||||||
|
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
|
||||||
|
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
|
||||||
|
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
|
||||||
|
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
|
||||||
|
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
|
||||||
|
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
|
||||||
|
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
|
||||||
|
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
|
||||||
|
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
|
||||||
|
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
|
||||||
|
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
|
||||||
|
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
|
||||||
|
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
|
||||||
|
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
|
||||||
|
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
|
||||||
|
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
|
||||||
|
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
|
||||||
|
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
|
||||||
|
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
|
||||||
|
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
|
||||||
|
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
|
||||||
|
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
|
||||||
|
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
|
||||||
|
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
|
||||||
|
0x2d02ef8dL
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
crc = crc ^ 0xffffffff;
|
||||||
|
|
||||||
|
while (len--)
|
||||||
|
crc = crc32_table[(crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
|
||||||
|
|
||||||
|
return crc ^ 0xffffffff;
|
||||||
|
}
|
|
@ -0,0 +1,516 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (encoding_utf.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <boolean.h>
|
||||||
|
#include <compat/strl.h>
|
||||||
|
#include <retro_inline.h>
|
||||||
|
|
||||||
|
#include <encodings/utf.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(_XBOX)
|
||||||
|
#include <windows.h>
|
||||||
|
#elif defined(_XBOX)
|
||||||
|
#include <xtl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static unsigned leading_ones(uint8_t c)
|
||||||
|
{
|
||||||
|
unsigned ones = 0;
|
||||||
|
while (c & 0x80)
|
||||||
|
{
|
||||||
|
ones++;
|
||||||
|
c <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ones;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple implementation. Assumes the sequence is
|
||||||
|
* properly synchronized and terminated. */
|
||||||
|
|
||||||
|
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
||||||
|
const char *in, size_t in_size)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
size_t ret = 0;
|
||||||
|
while (in_size && out_chars)
|
||||||
|
{
|
||||||
|
unsigned extra, shift;
|
||||||
|
uint32_t c;
|
||||||
|
uint8_t first = *in++;
|
||||||
|
unsigned ones = leading_ones(first);
|
||||||
|
|
||||||
|
if (ones > 6 || ones == 1) /* Invalid or desync. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
extra = ones ? ones - 1 : ones;
|
||||||
|
if (1 + extra > in_size) /* Overflow. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
shift = (extra - 1) * 6;
|
||||||
|
c = (first & ((1 << (7 - ones)) - 1)) << (6 * extra);
|
||||||
|
|
||||||
|
for (i = 0; i < extra; i++, in++, shift -= 6)
|
||||||
|
c |= (*in & 0x3f) << shift;
|
||||||
|
|
||||||
|
*out++ = c;
|
||||||
|
in_size -= 1 + extra;
|
||||||
|
out_chars--;
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||||
|
const uint16_t *in, size_t in_size)
|
||||||
|
{
|
||||||
|
static uint8_t kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||||
|
size_t out_pos = 0;
|
||||||
|
size_t in_pos = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned numAdds;
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
if (in_pos == in_size)
|
||||||
|
{
|
||||||
|
*out_chars = out_pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
value = in[in_pos++];
|
||||||
|
if (value < 0x80)
|
||||||
|
{
|
||||||
|
if (out)
|
||||||
|
out[out_pos] = (char)value;
|
||||||
|
out_pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 0xD800 && value < 0xE000)
|
||||||
|
{
|
||||||
|
uint32_t c2;
|
||||||
|
|
||||||
|
if (value >= 0xDC00 || in_pos == in_size)
|
||||||
|
break;
|
||||||
|
c2 = in[in_pos++];
|
||||||
|
if (c2 < 0xDC00 || c2 >= 0xE000)
|
||||||
|
break;
|
||||||
|
value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (numAdds = 1; numAdds < 5; numAdds++)
|
||||||
|
if (value < (((uint32_t)1) << (numAdds * 5 + 6)))
|
||||||
|
break;
|
||||||
|
if (out)
|
||||||
|
out[out_pos] = (char)(kUtf8Limits[numAdds - 1]
|
||||||
|
+ (value >> (6 * numAdds)));
|
||||||
|
out_pos++;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
numAdds--;
|
||||||
|
if (out)
|
||||||
|
out[out_pos] = (char)(0x80
|
||||||
|
+ ((value >> (6 * numAdds)) & 0x3F));
|
||||||
|
out_pos++;
|
||||||
|
}while (numAdds != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_chars = out_pos;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Acts mostly like strlcpy.
|
||||||
|
*
|
||||||
|
* Copies the given number of UTF-8 characters,
|
||||||
|
* but at most d_len bytes.
|
||||||
|
*
|
||||||
|
* Always NULL terminates.
|
||||||
|
* Does not copy half a character.
|
||||||
|
*
|
||||||
|
* Returns number of bytes. 's' is assumed valid UTF-8.
|
||||||
|
* Use only if 'chars' is considerably less than 'd_len'. */
|
||||||
|
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
|
||||||
|
{
|
||||||
|
const uint8_t *sb = (const uint8_t*)s;
|
||||||
|
const uint8_t *sb_org = sb;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (*sb && chars-- > 0)
|
||||||
|
{
|
||||||
|
sb++;
|
||||||
|
while ((*sb & 0xC0) == 0x80) sb++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)(sb - sb_org) > d_len-1 /* NUL */)
|
||||||
|
{
|
||||||
|
sb = sb_org + d_len-1;
|
||||||
|
while ((*sb & 0xC0) == 0x80) sb--;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(d, sb_org, sb-sb_org);
|
||||||
|
d[sb-sb_org] = '\0';
|
||||||
|
|
||||||
|
return sb-sb_org;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *utf8skip(const char *str, size_t chars)
|
||||||
|
{
|
||||||
|
const uint8_t *strb = (const uint8_t*)str;
|
||||||
|
if (!chars)
|
||||||
|
return str;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
strb++;
|
||||||
|
while ((*strb & 0xC0)==0x80) strb++;
|
||||||
|
chars--;
|
||||||
|
} while(chars);
|
||||||
|
return (const char*)strb;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t utf8len(const char *string)
|
||||||
|
{
|
||||||
|
size_t ret = 0;
|
||||||
|
|
||||||
|
if (!string)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (*string)
|
||||||
|
{
|
||||||
|
if ((*string & 0xC0) != 0x80)
|
||||||
|
ret++;
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t utf8_walkbyte(const char **string)
|
||||||
|
{
|
||||||
|
return *((*string)++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Does not validate the input, returns garbage if it's not UTF-8. */
|
||||||
|
uint32_t utf8_walk(const char **string)
|
||||||
|
{
|
||||||
|
uint8_t first = utf8_walkbyte(string);
|
||||||
|
uint32_t ret = 0;
|
||||||
|
|
||||||
|
if (first < 128)
|
||||||
|
return first;
|
||||||
|
|
||||||
|
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
|
||||||
|
if (first >= 0xE0)
|
||||||
|
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
|
||||||
|
if (first >= 0xF0)
|
||||||
|
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
|
||||||
|
|
||||||
|
if (first >= 0xF0)
|
||||||
|
return ret | (first & 7) << 18;
|
||||||
|
if (first >= 0xE0)
|
||||||
|
return ret | (first & 15) << 12;
|
||||||
|
return ret | (first & 31) << 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool utf16_to_char(uint8_t **utf_data,
|
||||||
|
size_t *dest_len, const uint16_t *in)
|
||||||
|
{
|
||||||
|
unsigned len = 0;
|
||||||
|
|
||||||
|
while (in[len] != '\0')
|
||||||
|
len++;
|
||||||
|
|
||||||
|
utf16_conv_utf8(NULL, dest_len, in, len);
|
||||||
|
*dest_len += 1;
|
||||||
|
*utf_data = (uint8_t*)malloc(*dest_len);
|
||||||
|
if (*utf_data == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return utf16_conv_utf8(*utf_data, dest_len, in, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
|
||||||
|
{
|
||||||
|
size_t dest_len = 0;
|
||||||
|
uint8_t *utf16_data = NULL;
|
||||||
|
bool ret = utf16_to_char(&utf16_data, &dest_len, in);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
utf16_data[dest_len] = 0;
|
||||||
|
strlcpy(s, (const char*)utf16_data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(utf16_data);
|
||||||
|
utf16_data = NULL;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||||
|
static char* mb_to_mb_string_alloc(const char *str,
|
||||||
|
enum CodePage cp_in, enum CodePage cp_out)
|
||||||
|
{
|
||||||
|
char *path_buf = NULL;
|
||||||
|
wchar_t *path_buf_wide = NULL;
|
||||||
|
int path_buf_len = 0;
|
||||||
|
int path_buf_wide_len = 0;
|
||||||
|
|
||||||
|
if (!str || !*str)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
(void)path_buf;
|
||||||
|
(void)path_buf_wide;
|
||||||
|
(void)path_buf_len;
|
||||||
|
(void)path_buf_wide_len;
|
||||||
|
|
||||||
|
#if !defined(_WIN32) || defined(_XBOX)
|
||||||
|
/* assume string needs no modification if not on Windows */
|
||||||
|
return strdup(str);
|
||||||
|
#else
|
||||||
|
#ifdef UNICODE
|
||||||
|
/* TODO/FIXME: Not implemented. */
|
||||||
|
return strdup(str);
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Windows 95 will return 0 from these functions with a UTF8 codepage set without MSLU. From an unknown MSDN version (others omit this info):
|
||||||
|
* - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-8. When this is set, dwFlags must be zero.
|
||||||
|
* - Windows 95: Under the Microsoft Layer for Unicode, MultiByteToWideChar also supports CP_UTF7 and CP_UTF8.
|
||||||
|
*/
|
||||||
|
path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0);
|
||||||
|
|
||||||
|
if (path_buf_wide_len)
|
||||||
|
{
|
||||||
|
path_buf_wide = (wchar_t*)
|
||||||
|
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (path_buf_wide)
|
||||||
|
{
|
||||||
|
MultiByteToWideChar(cp_in, 0,
|
||||||
|
str, -1, path_buf_wide, path_buf_wide_len);
|
||||||
|
|
||||||
|
if (*path_buf_wide)
|
||||||
|
{
|
||||||
|
path_buf_len = WideCharToMultiByte(cp_out, 0,
|
||||||
|
path_buf_wide, -1, NULL, 0, NULL, NULL);
|
||||||
|
|
||||||
|
if (path_buf_len)
|
||||||
|
{
|
||||||
|
path_buf = (char*)
|
||||||
|
calloc(path_buf_len + sizeof(char), sizeof(char));
|
||||||
|
|
||||||
|
if (path_buf)
|
||||||
|
{
|
||||||
|
WideCharToMultiByte(cp_out, 0,
|
||||||
|
path_buf_wide, -1, path_buf,
|
||||||
|
path_buf_len, NULL, NULL);
|
||||||
|
|
||||||
|
free(path_buf_wide);
|
||||||
|
|
||||||
|
if (*path_buf)
|
||||||
|
return path_buf;
|
||||||
|
|
||||||
|
free(path_buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(path_buf_wide);
|
||||||
|
return strdup(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return strdup(str);
|
||||||
|
|
||||||
|
if (path_buf_wide)
|
||||||
|
free(path_buf_wide);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||||
|
char* utf8_to_local_string_alloc(const char *str)
|
||||||
|
{
|
||||||
|
return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||||
|
char* local_to_utf8_string_alloc(const char *str)
|
||||||
|
{
|
||||||
|
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||||
|
wchar_t* utf8_to_utf16_string_alloc(const char *str)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
int len = 0;
|
||||||
|
int out_len = 0;
|
||||||
|
#else
|
||||||
|
size_t len = 0;
|
||||||
|
size_t out_len = 0;
|
||||||
|
#endif
|
||||||
|
wchar_t *buf = NULL;
|
||||||
|
|
||||||
|
if (!str || !*str)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* fallback to ANSI codepage instead */
|
||||||
|
len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_len < 0)
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
|
||||||
|
len = mbstowcs(NULL, str, 0) + 1;
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
out_len = mbstowcs(buf, str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_len == (size_t)-1)
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||||
|
char* utf16_to_utf8_string_alloc(const wchar_t *str)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
int len = 0;
|
||||||
|
int out_len = 0;
|
||||||
|
#else
|
||||||
|
size_t len = 0;
|
||||||
|
size_t out_len = 0;
|
||||||
|
#endif
|
||||||
|
char *buf = NULL;
|
||||||
|
|
||||||
|
if (!str || !*str)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
buf = (char*)calloc(len, sizeof(char));
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
out_len = WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, len, NULL, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* fallback to ANSI codepage instead */
|
||||||
|
len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
buf = (char*)calloc(len, sizeof(char));
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
out_len = WideCharToMultiByte(CP_ACP, 0, str, -1, buf, len, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_len < 0)
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
|
||||||
|
len = wcstombs(NULL, str, 0) + 1;
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
buf = (char*)calloc(len, sizeof(char));
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
out_len = wcstombs(buf, str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_len == (size_t)-1)
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
|
@ -0,0 +1,809 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (features_cpu.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <direct.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <compat/strl.h>
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <libretro.h>
|
||||||
|
#include <features/features_cpu.h>
|
||||||
|
#include <retro_timers.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(_XBOX)
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__CELLOS_LV2__)
|
||||||
|
#ifndef _PPU_INTRINSICS_H
|
||||||
|
#include <ppu_intrinsics.h>
|
||||||
|
#endif
|
||||||
|
#elif defined(_XBOX360)
|
||||||
|
#include <PPCIntrinsics.h>
|
||||||
|
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID) || defined(__QNX__) || defined(DJGPP)
|
||||||
|
/* POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present. */
|
||||||
|
#include <time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__QNX__) && !defined(CLOCK_MONOTONIC)
|
||||||
|
#define CLOCK_MONOTONIC 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PSP)
|
||||||
|
#include <pspkernel.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <psprtc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(VITA)
|
||||||
|
#include <psp2/kernel/processmgr.h>
|
||||||
|
#include <psp2/rtc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__PSL1GHT__)
|
||||||
|
#include <sys/time.h>
|
||||||
|
#elif defined(__CELLOS_LV2__)
|
||||||
|
#include <sys/sys_time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GEKKO
|
||||||
|
#include <ogc/lwp_watchdog.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIIU
|
||||||
|
#include <wiiu/os/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SWITCH
|
||||||
|
#include <libtransistor/types.h>
|
||||||
|
#include <libtransistor/svc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_3DS)
|
||||||
|
#include <3ds/svc.h>
|
||||||
|
#include <3ds/os.h>
|
||||||
|
#include <3ds/services/cfgu.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* iOS/OSX specific. Lacks clock_gettime(), so implement it. */
|
||||||
|
#ifdef __MACH__
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#ifndef CLOCK_MONOTONIC
|
||||||
|
#define CLOCK_MONOTONIC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_REALTIME
|
||||||
|
#define CLOCK_REALTIME 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* this function is part of iOS 10 now */
|
||||||
|
static int ra_clock_gettime(int clk_ik, struct timespec *t)
|
||||||
|
{
|
||||||
|
struct timeval now;
|
||||||
|
int rv = gettimeofday(&now, NULL);
|
||||||
|
if (rv)
|
||||||
|
return rv;
|
||||||
|
t->tv_sec = now.tv_sec;
|
||||||
|
t->tv_nsec = now.tv_usec * 1000;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__MACH__) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000
|
||||||
|
#else
|
||||||
|
#define ra_clock_gettime clock_gettime
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
#include <emscripten.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BSD) || defined(__APPLE__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cpu_features_get_perf_counter:
|
||||||
|
*
|
||||||
|
* Gets performance counter.
|
||||||
|
*
|
||||||
|
* Returns: performance counter.
|
||||||
|
**/
|
||||||
|
retro_perf_tick_t cpu_features_get_perf_counter(void)
|
||||||
|
{
|
||||||
|
retro_perf_tick_t time_ticks = 0;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
long tv_sec, tv_usec;
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||||
|
static const unsigned __int64 epoch = 11644473600000000;
|
||||||
|
#else
|
||||||
|
static const unsigned __int64 epoch = 11644473600000000ULL;
|
||||||
|
#endif
|
||||||
|
FILETIME file_time;
|
||||||
|
SYSTEMTIME system_time;
|
||||||
|
ULARGE_INTEGER ularge;
|
||||||
|
|
||||||
|
GetSystemTime(&system_time);
|
||||||
|
SystemTimeToFileTime(&system_time, &file_time);
|
||||||
|
ularge.LowPart = file_time.dwLowDateTime;
|
||||||
|
ularge.HighPart = file_time.dwHighDateTime;
|
||||||
|
|
||||||
|
tv_sec = (long)((ularge.QuadPart - epoch) / 10000000L);
|
||||||
|
tv_usec = (long)(system_time.wMilliseconds * 1000);
|
||||||
|
time_ticks = (1000000 * tv_sec + tv_usec);
|
||||||
|
#elif defined(__linux__) || defined(__QNX__) || defined(__MACH__)
|
||||||
|
struct timespec tv = {0};
|
||||||
|
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) == 0)
|
||||||
|
time_ticks = (retro_perf_tick_t)tv.tv_sec * 1000000000 +
|
||||||
|
(retro_perf_tick_t)tv.tv_nsec;
|
||||||
|
|
||||||
|
#elif defined(__GNUC__) && defined(__i386__) || defined(__i486__) || defined(__i686__)
|
||||||
|
__asm__ volatile ("rdtsc" : "=A" (time_ticks));
|
||||||
|
#elif defined(__GNUC__) && defined(__x86_64__)
|
||||||
|
unsigned a, d;
|
||||||
|
__asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
|
||||||
|
time_ticks = (retro_perf_tick_t)a | ((retro_perf_tick_t)d << 32);
|
||||||
|
#elif defined(__ARM_ARCH_6__)
|
||||||
|
__asm__ volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"(time_ticks) );
|
||||||
|
#elif defined(__CELLOS_LV2__) || defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__)
|
||||||
|
time_ticks = __mftb();
|
||||||
|
#elif defined(GEKKO)
|
||||||
|
time_ticks = gettime();
|
||||||
|
#elif defined(PSP)
|
||||||
|
sceRtcGetCurrentTick((uint64_t*)&time_ticks);
|
||||||
|
#elif defined(VITA)
|
||||||
|
sceRtcGetCurrentTick((SceRtcTick*)&time_ticks);
|
||||||
|
#elif defined(_3DS)
|
||||||
|
time_ticks = svcGetSystemTick();
|
||||||
|
#elif defined(WIIU)
|
||||||
|
time_ticks = OSGetSystemTime();
|
||||||
|
#elif defined(__mips__)
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv,NULL);
|
||||||
|
time_ticks = (1000000 * tv.tv_sec + tv.tv_usec);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return time_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cpu_features_get_time_usec:
|
||||||
|
*
|
||||||
|
* Gets time in microseconds.
|
||||||
|
*
|
||||||
|
* Returns: time in microseconds.
|
||||||
|
**/
|
||||||
|
retro_time_t cpu_features_get_time_usec(void)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
static LARGE_INTEGER freq;
|
||||||
|
LARGE_INTEGER count;
|
||||||
|
|
||||||
|
/* Frequency is guaranteed to not change. */
|
||||||
|
if (!freq.QuadPart && !QueryPerformanceFrequency(&freq))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!QueryPerformanceCounter(&count))
|
||||||
|
return 0;
|
||||||
|
return count.QuadPart * 1000000 / freq.QuadPart;
|
||||||
|
#elif defined(__CELLOS_LV2__)
|
||||||
|
return sys_time_get_system_time();
|
||||||
|
#elif defined(GEKKO)
|
||||||
|
return ticks_to_microsecs(gettime());
|
||||||
|
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__)
|
||||||
|
struct timespec tv = {0};
|
||||||
|
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) < 0)
|
||||||
|
return 0;
|
||||||
|
return tv.tv_sec * INT64_C(1000000) + (tv.tv_nsec + 500) / 1000;
|
||||||
|
#elif defined(EMSCRIPTEN)
|
||||||
|
return emscripten_get_now() * 1000;
|
||||||
|
#elif defined(__mips__) || defined(DJGPP)
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv,NULL);
|
||||||
|
return (1000000 * tv.tv_sec + tv.tv_usec);
|
||||||
|
#elif defined(_3DS)
|
||||||
|
return osGetTime() * 1000;
|
||||||
|
#elif defined(VITA)
|
||||||
|
return sceKernelGetProcessTimeWide();
|
||||||
|
#elif defined(WIIU)
|
||||||
|
return ticks_to_us(OSGetSystemTime());
|
||||||
|
#elif defined(SWITCH)
|
||||||
|
return (svcGetSystemTick() * 10) / 192;
|
||||||
|
#else
|
||||||
|
#error "Your platform does not have a timer function implemented in cpu_features_get_time_usec(). Cannot continue."
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i686__) || (defined(_M_X64) && _MSC_VER > 1310) || (defined(_M_IX86) && _MSC_VER > 1310)
|
||||||
|
#define CPU_X86
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(_XBOX)
|
||||||
|
#if (_MSC_VER > 1310)
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CPU_X86) && !defined(__MACH__)
|
||||||
|
void x86_cpuid(int func, int flags[4])
|
||||||
|
{
|
||||||
|
/* On Android, we compile RetroArch with PIC, and we
|
||||||
|
* are not allowed to clobber the ebx register. */
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#define REG_b "rbx"
|
||||||
|
#define REG_S "rsi"
|
||||||
|
#else
|
||||||
|
#define REG_b "ebx"
|
||||||
|
#define REG_S "esi"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile (
|
||||||
|
"mov %%" REG_b ", %%" REG_S "\n"
|
||||||
|
"cpuid\n"
|
||||||
|
"xchg %%" REG_b ", %%" REG_S "\n"
|
||||||
|
: "=a"(flags[0]), "=S"(flags[1]), "=c"(flags[2]), "=d"(flags[3])
|
||||||
|
: "a"(func));
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
__cpuid(flags, func);
|
||||||
|
#else
|
||||||
|
printf("Unknown compiler. Cannot check CPUID with inline assembly.\n");
|
||||||
|
memset(flags, 0, 4 * sizeof(int));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only runs on i686 and above. Needs to be conditionally run. */
|
||||||
|
static uint64_t xgetbv_x86(uint32_t idx)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
uint32_t eax, edx;
|
||||||
|
__asm__ volatile (
|
||||||
|
/* Older GCC versions (Apple's GCC for example) do
|
||||||
|
* not understand xgetbv instruction.
|
||||||
|
* Stamp out the machine code directly.
|
||||||
|
*/
|
||||||
|
".byte 0x0f, 0x01, 0xd0\n"
|
||||||
|
: "=a"(eax), "=d"(edx) : "c"(idx));
|
||||||
|
return ((uint64_t)edx << 32) | eax;
|
||||||
|
#elif _MSC_FULL_VER >= 160040219
|
||||||
|
/* Intrinsic only works on 2010 SP1 and above. */
|
||||||
|
return _xgetbv(idx);
|
||||||
|
#else
|
||||||
|
printf("Unknown compiler. Cannot check xgetbv bits.\n");
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ARM_NEON__)
|
||||||
|
static void arm_enable_runfast_mode(void)
|
||||||
|
{
|
||||||
|
/* RunFast mode. Enables flush-to-zero and some
|
||||||
|
* floating point optimizations. */
|
||||||
|
static const unsigned x = 0x04086060;
|
||||||
|
static const unsigned y = 0x03000000;
|
||||||
|
int r;
|
||||||
|
__asm__ volatile(
|
||||||
|
"fmrx %0, fpscr \n\t" /* r0 = FPSCR */
|
||||||
|
"and %0, %0, %1 \n\t" /* r0 = r0 & 0x04086060 */
|
||||||
|
"orr %0, %0, %2 \n\t" /* r0 = r0 | 0x03000000 */
|
||||||
|
"fmxr fpscr, %0 \n\t" /* FPSCR = r0 */
|
||||||
|
: "=r"(r)
|
||||||
|
: "r"(x), "r"(y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) && !defined(CPU_X86)
|
||||||
|
static unsigned char check_arm_cpu_feature(const char* feature)
|
||||||
|
{
|
||||||
|
char line[1024];
|
||||||
|
unsigned char status = 0;
|
||||||
|
RFILE *fp = filestream_open("/proc/cpuinfo",
|
||||||
|
RETRO_VFS_FILE_ACCESS_READ,
|
||||||
|
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||||
|
|
||||||
|
if (!fp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (filestream_gets(fp, line, sizeof(line)) != NULL)
|
||||||
|
{
|
||||||
|
if (strncmp(line, "Features\t: ", 11))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strstr(line + 11, feature) != NULL)
|
||||||
|
status = 1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
filestream_close(fp);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(_SC_NPROCESSORS_ONLN)
|
||||||
|
/* Parse an decimal integer starting from 'input', but not going further
|
||||||
|
* than 'limit'. Return the value into '*result'.
|
||||||
|
*
|
||||||
|
* NOTE: Does not skip over leading spaces, or deal with sign characters.
|
||||||
|
* NOTE: Ignores overflows.
|
||||||
|
*
|
||||||
|
* The function returns NULL in case of error (bad format), or the new
|
||||||
|
* position after the decimal number in case of success (which will always
|
||||||
|
* be <= 'limit').
|
||||||
|
*/
|
||||||
|
static const char *parse_decimal(const char* input,
|
||||||
|
const char* limit, int* result)
|
||||||
|
{
|
||||||
|
const char* p = input;
|
||||||
|
int val = 0;
|
||||||
|
|
||||||
|
while (p < limit)
|
||||||
|
{
|
||||||
|
int d = (*p - '0');
|
||||||
|
if ((unsigned)d >= 10U)
|
||||||
|
break;
|
||||||
|
val = val*10 + d;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (p == input)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*result = val;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a textual list of cpus and store the result inside a CpuList object.
|
||||||
|
* Input format is the following:
|
||||||
|
* - comma-separated list of items (no spaces)
|
||||||
|
* - each item is either a single decimal number (cpu index), or a range made
|
||||||
|
* of two numbers separated by a single dash (-). Ranges are inclusive.
|
||||||
|
*
|
||||||
|
* Examples: 0
|
||||||
|
* 2,4-127,128-143
|
||||||
|
* 0-1
|
||||||
|
*/
|
||||||
|
static void cpulist_parse(CpuList* list, char **buf, ssize_t length)
|
||||||
|
{
|
||||||
|
const char* p = (const char*)buf;
|
||||||
|
const char* end = p + length;
|
||||||
|
|
||||||
|
/* NOTE: the input line coming from sysfs typically contains a
|
||||||
|
* trailing newline, so take care of it in the code below
|
||||||
|
*/
|
||||||
|
while (p < end && *p != '\n')
|
||||||
|
{
|
||||||
|
int val, start_value, end_value;
|
||||||
|
/* Find the end of current item, and put it into 'q' */
|
||||||
|
const char *q = (const char*)memchr(p, ',', end-p);
|
||||||
|
|
||||||
|
if (!q)
|
||||||
|
q = end;
|
||||||
|
|
||||||
|
/* Get first value */
|
||||||
|
p = parse_decimal(p, q, &start_value);
|
||||||
|
if (p == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
end_value = start_value;
|
||||||
|
|
||||||
|
/* If we're not at the end of the item, expect a dash and
|
||||||
|
* and integer; extract end value.
|
||||||
|
*/
|
||||||
|
if (p < q && *p == '-')
|
||||||
|
{
|
||||||
|
p = parse_decimal(p+1, q, &end_value);
|
||||||
|
if (p == NULL)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set bits CPU list bits */
|
||||||
|
for (val = start_value; val <= end_value; val++)
|
||||||
|
{
|
||||||
|
if ((unsigned)val < 32)
|
||||||
|
list->mask |= (uint32_t)(1U << val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Jump to next item */
|
||||||
|
p = q;
|
||||||
|
if (p < end)
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a CPU list from one sysfs file */
|
||||||
|
static void cpulist_read_from(CpuList* list, const char* filename)
|
||||||
|
{
|
||||||
|
ssize_t length;
|
||||||
|
char *buf = NULL;
|
||||||
|
|
||||||
|
list->mask = 0;
|
||||||
|
|
||||||
|
if (filestream_read_file(filename, (void**)&buf, &length) != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cpulist_parse(list, &buf, length);
|
||||||
|
if (buf)
|
||||||
|
free(buf);
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cpu_features_get_core_amount:
|
||||||
|
*
|
||||||
|
* Gets the amount of available CPU cores.
|
||||||
|
*
|
||||||
|
* Returns: amount of CPU cores available.
|
||||||
|
**/
|
||||||
|
unsigned cpu_features_get_core_amount(void)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) && !defined(_XBOX)
|
||||||
|
/* Win32 */
|
||||||
|
SYSTEM_INFO sysinfo;
|
||||||
|
GetSystemInfo(&sysinfo);
|
||||||
|
return sysinfo.dwNumberOfProcessors;
|
||||||
|
#elif defined(GEKKO)
|
||||||
|
return 1;
|
||||||
|
#elif defined(PSP)
|
||||||
|
return 1;
|
||||||
|
#elif defined(VITA)
|
||||||
|
return 4;
|
||||||
|
#elif defined(_3DS)
|
||||||
|
u8 device_model = 0xFF;
|
||||||
|
CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/
|
||||||
|
switch (device_model)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 3:
|
||||||
|
/*Old 3/2DS*/
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
/*New 3/2DS*/
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/*Unknown Device Or Check Failed*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
#elif defined(WIIU)
|
||||||
|
return 3;
|
||||||
|
#elif defined(_SC_NPROCESSORS_ONLN)
|
||||||
|
/* Linux, most UNIX-likes. */
|
||||||
|
long ret = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
if (ret <= 0)
|
||||||
|
return (unsigned)1;
|
||||||
|
return (unsigned)ret;
|
||||||
|
#elif defined(BSD) || defined(__APPLE__)
|
||||||
|
/* BSD */
|
||||||
|
/* Copypasta from stackoverflow, dunno if it works. */
|
||||||
|
int num_cpu = 0;
|
||||||
|
int mib[4];
|
||||||
|
size_t len = sizeof(num_cpu);
|
||||||
|
|
||||||
|
mib[0] = CTL_HW;
|
||||||
|
mib[1] = HW_AVAILCPU;
|
||||||
|
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
|
||||||
|
if (num_cpu < 1)
|
||||||
|
{
|
||||||
|
mib[1] = HW_NCPU;
|
||||||
|
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
|
||||||
|
if (num_cpu < 1)
|
||||||
|
num_cpu = 1;
|
||||||
|
}
|
||||||
|
return num_cpu;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
CpuList cpus_present[1];
|
||||||
|
CpuList cpus_possible[1];
|
||||||
|
int amount = 0;
|
||||||
|
|
||||||
|
cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present");
|
||||||
|
cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible");
|
||||||
|
|
||||||
|
/* Compute the intersection of both sets to get the actual number of
|
||||||
|
* CPU cores that can be used on this device by the kernel.
|
||||||
|
*/
|
||||||
|
cpus_present->mask &= cpus_possible->mask;
|
||||||
|
amount = __builtin_popcount(cpus_present->mask);
|
||||||
|
|
||||||
|
if (amount == 0)
|
||||||
|
return 1;
|
||||||
|
return amount;
|
||||||
|
#elif defined(_XBOX360)
|
||||||
|
return 3;
|
||||||
|
#else
|
||||||
|
/* No idea, assume single core. */
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* According to http://en.wikipedia.org/wiki/CPUID */
|
||||||
|
#define VENDOR_INTEL_b 0x756e6547
|
||||||
|
#define VENDOR_INTEL_c 0x6c65746e
|
||||||
|
#define VENDOR_INTEL_d 0x49656e69
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cpu_features_get:
|
||||||
|
*
|
||||||
|
* Gets CPU features..
|
||||||
|
*
|
||||||
|
* Returns: bitmask of all CPU features available.
|
||||||
|
**/
|
||||||
|
uint64_t cpu_features_get(void)
|
||||||
|
{
|
||||||
|
int flags[4];
|
||||||
|
int vendor_shuffle[3];
|
||||||
|
char vendor[13];
|
||||||
|
size_t len = 0;
|
||||||
|
uint64_t cpu_flags = 0;
|
||||||
|
uint64_t cpu = 0;
|
||||||
|
unsigned max_flag = 0;
|
||||||
|
#if defined(CPU_X86) && !defined(__MACH__)
|
||||||
|
int vendor_is_intel = 0;
|
||||||
|
const int avx_flags = (1 << 27) | (1 << 28);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char buf[sizeof(" MMX MMXEXT SSE SSE2 SSE3 SSSE3 SS4 SSE4.2 AES AVX AVX2 NEON VMX VMX128 VFPU PS")];
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
(void)len;
|
||||||
|
(void)cpu_flags;
|
||||||
|
(void)flags;
|
||||||
|
(void)max_flag;
|
||||||
|
(void)vendor;
|
||||||
|
(void)vendor_shuffle;
|
||||||
|
|
||||||
|
#if defined(__MACH__)
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.mmx", NULL, &len, NULL, 0) == 0)
|
||||||
|
{
|
||||||
|
cpu |= RETRO_SIMD_MMX;
|
||||||
|
cpu |= RETRO_SIMD_MMXEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.floatingpoint", NULL, &len, NULL, 0) == 0)
|
||||||
|
{
|
||||||
|
cpu |= RETRO_SIMD_CMOV;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.sse", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_SSE;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.sse2", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_SSE2;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.sse3", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_SSE3;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.supplementalsse3", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_SSSE3;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.sse4_1", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_SSE4;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.sse4_2", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_SSE42;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.aes", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_AES;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.avx1_0", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_AVX;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.avx2_0", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_AVX2;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.altivec", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_VMX;
|
||||||
|
|
||||||
|
len = sizeof(size_t);
|
||||||
|
if (sysctlbyname("hw.optional.neon", NULL, &len, NULL, 0) == 0)
|
||||||
|
cpu |= RETRO_SIMD_NEON;
|
||||||
|
|
||||||
|
#elif defined(CPU_X86)
|
||||||
|
(void)avx_flags;
|
||||||
|
|
||||||
|
x86_cpuid(0, flags);
|
||||||
|
vendor_shuffle[0] = flags[1];
|
||||||
|
vendor_shuffle[1] = flags[3];
|
||||||
|
vendor_shuffle[2] = flags[2];
|
||||||
|
|
||||||
|
vendor[0] = '\0';
|
||||||
|
memcpy(vendor, vendor_shuffle, sizeof(vendor_shuffle));
|
||||||
|
|
||||||
|
/* printf("[CPUID]: Vendor: %s\n", vendor); */
|
||||||
|
|
||||||
|
vendor_is_intel = (
|
||||||
|
flags[1] == VENDOR_INTEL_b &&
|
||||||
|
flags[2] == VENDOR_INTEL_c &&
|
||||||
|
flags[3] == VENDOR_INTEL_d);
|
||||||
|
|
||||||
|
max_flag = flags[0];
|
||||||
|
if (max_flag < 1) /* Does CPUID not support func = 1? (unlikely ...) */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
x86_cpuid(1, flags);
|
||||||
|
|
||||||
|
if (flags[3] & (1 << 15))
|
||||||
|
cpu |= RETRO_SIMD_CMOV;
|
||||||
|
|
||||||
|
if (flags[3] & (1 << 23))
|
||||||
|
cpu |= RETRO_SIMD_MMX;
|
||||||
|
|
||||||
|
if (flags[3] & (1 << 25))
|
||||||
|
{
|
||||||
|
/* SSE also implies MMXEXT (according to FFmpeg source). */
|
||||||
|
cpu |= RETRO_SIMD_SSE;
|
||||||
|
cpu |= RETRO_SIMD_MMXEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (flags[3] & (1 << 26))
|
||||||
|
cpu |= RETRO_SIMD_SSE2;
|
||||||
|
|
||||||
|
if (flags[2] & (1 << 0))
|
||||||
|
cpu |= RETRO_SIMD_SSE3;
|
||||||
|
|
||||||
|
if (flags[2] & (1 << 9))
|
||||||
|
cpu |= RETRO_SIMD_SSSE3;
|
||||||
|
|
||||||
|
if (flags[2] & (1 << 19))
|
||||||
|
cpu |= RETRO_SIMD_SSE4;
|
||||||
|
|
||||||
|
if (flags[2] & (1 << 20))
|
||||||
|
cpu |= RETRO_SIMD_SSE42;
|
||||||
|
|
||||||
|
if ((flags[2] & (1 << 23)))
|
||||||
|
cpu |= RETRO_SIMD_POPCNT;
|
||||||
|
|
||||||
|
if (vendor_is_intel && (flags[2] & (1 << 22)))
|
||||||
|
cpu |= RETRO_SIMD_MOVBE;
|
||||||
|
|
||||||
|
if (flags[2] & (1 << 25))
|
||||||
|
cpu |= RETRO_SIMD_AES;
|
||||||
|
|
||||||
|
|
||||||
|
/* Must only perform xgetbv check if we have
|
||||||
|
* AVX CPU support (guaranteed to have at least i686). */
|
||||||
|
if (((flags[2] & avx_flags) == avx_flags)
|
||||||
|
&& ((xgetbv_x86(0) & 0x6) == 0x6))
|
||||||
|
cpu |= RETRO_SIMD_AVX;
|
||||||
|
|
||||||
|
if (max_flag >= 7)
|
||||||
|
{
|
||||||
|
x86_cpuid(7, flags);
|
||||||
|
if (flags[1] & (1 << 5))
|
||||||
|
cpu |= RETRO_SIMD_AVX2;
|
||||||
|
}
|
||||||
|
|
||||||
|
x86_cpuid(0x80000000, flags);
|
||||||
|
max_flag = flags[0];
|
||||||
|
if (max_flag >= 0x80000001u)
|
||||||
|
{
|
||||||
|
x86_cpuid(0x80000001, flags);
|
||||||
|
if (flags[3] & (1 << 23))
|
||||||
|
cpu |= RETRO_SIMD_MMX;
|
||||||
|
if (flags[3] & (1 << 22))
|
||||||
|
cpu |= RETRO_SIMD_MMXEXT;
|
||||||
|
}
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (check_arm_cpu_feature("neon"))
|
||||||
|
{
|
||||||
|
cpu |= RETRO_SIMD_NEON;
|
||||||
|
#ifdef __ARM_NEON__
|
||||||
|
arm_enable_runfast_mode();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_arm_cpu_feature("vfpv3"))
|
||||||
|
cpu |= RETRO_SIMD_VFPV3;
|
||||||
|
|
||||||
|
if (check_arm_cpu_feature("vfpv4"))
|
||||||
|
cpu |= RETRO_SIMD_VFPV4;
|
||||||
|
|
||||||
|
if (check_arm_cpu_feature("asimd"))
|
||||||
|
{
|
||||||
|
cpu |= RETRO_SIMD_ASIMD;
|
||||||
|
#ifdef __ARM_NEON__
|
||||||
|
cpu |= RETRO_SIMD_NEON;
|
||||||
|
arm_enable_runfast_mode();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
check_arm_cpu_feature("swp");
|
||||||
|
check_arm_cpu_feature("half");
|
||||||
|
check_arm_cpu_feature("thumb");
|
||||||
|
check_arm_cpu_feature("fastmult");
|
||||||
|
check_arm_cpu_feature("vfp");
|
||||||
|
check_arm_cpu_feature("edsp");
|
||||||
|
check_arm_cpu_feature("thumbee");
|
||||||
|
check_arm_cpu_feature("tls");
|
||||||
|
check_arm_cpu_feature("idiva");
|
||||||
|
check_arm_cpu_feature("idivt");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(__ARM_NEON__)
|
||||||
|
cpu |= RETRO_SIMD_NEON;
|
||||||
|
arm_enable_runfast_mode();
|
||||||
|
#elif defined(__ALTIVEC__)
|
||||||
|
cpu |= RETRO_SIMD_VMX;
|
||||||
|
#elif defined(XBOX360)
|
||||||
|
cpu |= RETRO_SIMD_VMX128;
|
||||||
|
#elif defined(PSP)
|
||||||
|
cpu |= RETRO_SIMD_VFPU;
|
||||||
|
#elif defined(GEKKO)
|
||||||
|
cpu |= RETRO_SIMD_PS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (cpu & RETRO_SIMD_MMX) strlcat(buf, " MMX", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_MMXEXT) strlcat(buf, " MMXEXT", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_SSE) strlcat(buf, " SSE", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_SSE2) strlcat(buf, " SSE2", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_SSE3) strlcat(buf, " SSE3", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_SSSE3) strlcat(buf, " SSSE3", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_SSE4) strlcat(buf, " SSE4", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_SSE42) strlcat(buf, " SSE4.2", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_AES) strlcat(buf, " AES", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_AVX) strlcat(buf, " AVX", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_AVX2) strlcat(buf, " AVX2", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_NEON) strlcat(buf, " NEON", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_VFPV3) strlcat(buf, " VFPv3", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_VFPV4) strlcat(buf, " VFPv4", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_VMX) strlcat(buf, " VMX", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_VMX128) strlcat(buf, " VMX128", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_VFPU) strlcat(buf, " VFPU", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_PS) strlcat(buf, " PS", sizeof(buf));
|
||||||
|
if (cpu & RETRO_SIMD_ASIMD) strlcat(buf, " ASIMD", sizeof(buf));
|
||||||
|
|
||||||
|
return cpu;
|
||||||
|
}
|
|
@ -0,0 +1,897 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (archive_file.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <compat/strl.h>
|
||||||
|
#include <file/archive_file.h>
|
||||||
|
#include <file/file_path.h>
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <lists/string_list.h>
|
||||||
|
#include <string/stdstring.h>
|
||||||
|
|
||||||
|
struct file_archive_file_data
|
||||||
|
{
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
int fd;
|
||||||
|
#endif
|
||||||
|
void *data;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t file_archive_size(file_archive_file_data_t *data)
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
return 0;
|
||||||
|
return data->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t *file_archive_data(file_archive_file_data_t *data)
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
return NULL;
|
||||||
|
return (const uint8_t*)data->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
/* Closes, unmaps and frees. */
|
||||||
|
static void file_archive_free(file_archive_file_data_t *data)
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (data->data)
|
||||||
|
munmap(data->data, data->size);
|
||||||
|
if (data->fd >= 0)
|
||||||
|
close(data->fd);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static file_archive_file_data_t* file_archive_open(const char *path)
|
||||||
|
{
|
||||||
|
file_archive_file_data_t *data = (file_archive_file_data_t*)calloc(1, sizeof(*data));
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data->fd = open(path, O_RDONLY);
|
||||||
|
|
||||||
|
/* Failed to open archive. */
|
||||||
|
if (data->fd < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
data->size = path_get_size(path);
|
||||||
|
if (!data->size)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
data->data = mmap(NULL, data->size, PROT_READ, MAP_SHARED, data->fd, 0);
|
||||||
|
if (data->data == MAP_FAILED)
|
||||||
|
{
|
||||||
|
data->data = NULL;
|
||||||
|
|
||||||
|
/* Failed to mmap() file */
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
|
||||||
|
error:
|
||||||
|
file_archive_free(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Closes, unmaps and frees. */
|
||||||
|
static void file_archive_free(file_archive_file_data_t *data)
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
if(data->data)
|
||||||
|
free(data->data);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static file_archive_file_data_t* file_archive_open(const char *path)
|
||||||
|
{
|
||||||
|
ssize_t ret = -1;
|
||||||
|
bool read_from_file = false;
|
||||||
|
file_archive_file_data_t *data = (file_archive_file_data_t*)
|
||||||
|
calloc(1, sizeof(*data));
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
read_from_file = filestream_read_file(path, &data->data, &ret);
|
||||||
|
|
||||||
|
/* Failed to open archive? */
|
||||||
|
if (!read_from_file || ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
data->size = ret;
|
||||||
|
return data;
|
||||||
|
|
||||||
|
error:
|
||||||
|
file_archive_free(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int file_archive_get_file_list_cb(
|
||||||
|
const char *path,
|
||||||
|
const char *valid_exts,
|
||||||
|
const uint8_t *cdata,
|
||||||
|
unsigned cmode,
|
||||||
|
uint32_t csize,
|
||||||
|
uint32_t size,
|
||||||
|
uint32_t checksum,
|
||||||
|
struct archive_extract_userdata *userdata)
|
||||||
|
{
|
||||||
|
union string_list_elem_attr attr;
|
||||||
|
int ret = 0;
|
||||||
|
struct string_list *ext_list = NULL;
|
||||||
|
size_t path_len = strlen(path);
|
||||||
|
|
||||||
|
(void)cdata;
|
||||||
|
(void)cmode;
|
||||||
|
(void)csize;
|
||||||
|
(void)size;
|
||||||
|
(void)checksum;
|
||||||
|
|
||||||
|
attr.i = 0;
|
||||||
|
|
||||||
|
if (!path_len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (valid_exts)
|
||||||
|
ext_list = string_split(valid_exts, "|");
|
||||||
|
|
||||||
|
if (ext_list)
|
||||||
|
{
|
||||||
|
const char *file_ext = NULL;
|
||||||
|
/* Checks if this entry is a directory or a file. */
|
||||||
|
char last_char = path[path_len-1];
|
||||||
|
|
||||||
|
/* Skip if directory. */
|
||||||
|
if (last_char == '/' || last_char == '\\' )
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
file_ext = path_get_extension(path);
|
||||||
|
|
||||||
|
if (!file_ext)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!string_list_find_elem_prefix(ext_list, ".", file_ext))
|
||||||
|
{
|
||||||
|
/* keep iterating */
|
||||||
|
ret = -1;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE;
|
||||||
|
string_list_free(ext_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string_list_append(userdata->list, path, attr);
|
||||||
|
|
||||||
|
error:
|
||||||
|
string_list_free(ext_list);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int file_archive_extract_cb(const char *name, const char *valid_exts,
|
||||||
|
const uint8_t *cdata,
|
||||||
|
unsigned cmode, uint32_t csize, uint32_t size,
|
||||||
|
uint32_t checksum, struct archive_extract_userdata *userdata)
|
||||||
|
{
|
||||||
|
const char *ext = path_get_extension(name);
|
||||||
|
|
||||||
|
/* Extract first file that matches our list. */
|
||||||
|
if (ext && string_list_find_elem(userdata->ext, ext))
|
||||||
|
{
|
||||||
|
char new_path[PATH_MAX_LENGTH];
|
||||||
|
char wanted_file[PATH_MAX_LENGTH];
|
||||||
|
const char *delim = NULL;
|
||||||
|
|
||||||
|
new_path[0] = wanted_file[0] = '\0';
|
||||||
|
|
||||||
|
if (userdata->extraction_directory)
|
||||||
|
fill_pathname_join(new_path, userdata->extraction_directory,
|
||||||
|
path_basename(name), sizeof(new_path));
|
||||||
|
else
|
||||||
|
fill_pathname_resolve_relative(new_path, userdata->archive_path,
|
||||||
|
path_basename(name), sizeof(new_path));
|
||||||
|
|
||||||
|
userdata->first_extracted_file_path = strdup(new_path);
|
||||||
|
|
||||||
|
delim = path_get_archive_delim(userdata->archive_path);
|
||||||
|
|
||||||
|
if (delim)
|
||||||
|
{
|
||||||
|
strlcpy(wanted_file, delim + 1, sizeof(wanted_file));
|
||||||
|
|
||||||
|
if (!string_is_equal_noncase(userdata->extracted_file_path,
|
||||||
|
wanted_file))
|
||||||
|
return 1; /* keep searching for the right file */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
strlcpy(wanted_file, userdata->archive_path, sizeof(wanted_file));
|
||||||
|
|
||||||
|
if (file_archive_perform_mode(new_path,
|
||||||
|
valid_exts, cdata, cmode, csize, size,
|
||||||
|
0, userdata))
|
||||||
|
userdata->found_file = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int file_archive_parse_file_init(file_archive_transfer_t *state,
|
||||||
|
const char *file)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX_LENGTH];
|
||||||
|
char *last = NULL;
|
||||||
|
|
||||||
|
path[0] = '\0';
|
||||||
|
|
||||||
|
strlcpy(path, file, sizeof(path));
|
||||||
|
|
||||||
|
last = (char*)path_get_archive_delim(path);
|
||||||
|
|
||||||
|
if (last)
|
||||||
|
*last = '\0';
|
||||||
|
|
||||||
|
state->backend = file_archive_get_file_backend(path);
|
||||||
|
if (!state->backend)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
state->handle = file_archive_open(path);
|
||||||
|
if (!state->handle)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
state->archive_size = (int32_t)file_archive_size(state->handle);
|
||||||
|
state->data = file_archive_data(state->handle);
|
||||||
|
state->footer = 0;
|
||||||
|
state->directory = 0;
|
||||||
|
|
||||||
|
return state->backend->archive_parse_file_init(state, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* file_archive_decompress_data_to_file:
|
||||||
|
* @path : filename path of archive.
|
||||||
|
* @valid_exts : Valid extensions of archive to be parsed.
|
||||||
|
* If NULL, allow all.
|
||||||
|
* @cdata : input data.
|
||||||
|
* @csize : size of input data.
|
||||||
|
* @size : output file size
|
||||||
|
* @checksum : CRC32 checksum from input data.
|
||||||
|
*
|
||||||
|
* Decompress data to file.
|
||||||
|
*
|
||||||
|
* Returns: true (1) on success, otherwise false (0).
|
||||||
|
**/
|
||||||
|
static int file_archive_decompress_data_to_file(
|
||||||
|
file_archive_file_handle_t *handle,
|
||||||
|
int ret,
|
||||||
|
const char *path,
|
||||||
|
const char *valid_exts,
|
||||||
|
const uint8_t *cdata,
|
||||||
|
uint32_t csize,
|
||||||
|
uint32_t size,
|
||||||
|
uint32_t checksum)
|
||||||
|
{
|
||||||
|
if (!handle || ret == -1)
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
handle->real_checksum = handle->backend->stream_crc_calculate(
|
||||||
|
0, handle->data, size);
|
||||||
|
if (handle->real_checksum != checksum)
|
||||||
|
{
|
||||||
|
/* File CRC difers from archive CRC. */
|
||||||
|
printf("File CRC differs from archive CRC. File: 0x%x, Archive: 0x%x.\n",
|
||||||
|
(unsigned)handle->real_checksum, (unsigned)checksum);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!filestream_write_file(path, handle->data, size))
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
if (handle)
|
||||||
|
{
|
||||||
|
if (handle->backend)
|
||||||
|
{
|
||||||
|
if (handle->backend->stream_free)
|
||||||
|
handle->backend->stream_free(handle->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle->data)
|
||||||
|
free(handle->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state)
|
||||||
|
{
|
||||||
|
if (!state || !state->handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state->type = ARCHIVE_TRANSFER_DEINIT;
|
||||||
|
file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_archive_parse_file_iterate(
|
||||||
|
file_archive_transfer_t *state,
|
||||||
|
bool *returnerr,
|
||||||
|
const char *file,
|
||||||
|
const char *valid_exts,
|
||||||
|
file_archive_file_cb file_cb,
|
||||||
|
struct archive_extract_userdata *userdata)
|
||||||
|
{
|
||||||
|
if (!state)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
switch (state->type)
|
||||||
|
{
|
||||||
|
case ARCHIVE_TRANSFER_NONE:
|
||||||
|
break;
|
||||||
|
case ARCHIVE_TRANSFER_INIT:
|
||||||
|
if (file_archive_parse_file_init(state, file) == 0)
|
||||||
|
{
|
||||||
|
if (userdata)
|
||||||
|
{
|
||||||
|
userdata->context = state->stream;
|
||||||
|
strlcpy(userdata->archive_path, file,
|
||||||
|
sizeof(userdata->archive_path));
|
||||||
|
}
|
||||||
|
state->type = ARCHIVE_TRANSFER_ITERATE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
state->type = ARCHIVE_TRANSFER_DEINIT_ERROR;
|
||||||
|
break;
|
||||||
|
case ARCHIVE_TRANSFER_ITERATE:
|
||||||
|
if (file_archive_get_file_backend(file))
|
||||||
|
{
|
||||||
|
const struct file_archive_file_backend *backend =
|
||||||
|
file_archive_get_file_backend(file);
|
||||||
|
int ret =
|
||||||
|
backend->archive_parse_file_iterate_step(state,
|
||||||
|
valid_exts, userdata, file_cb);
|
||||||
|
|
||||||
|
if (ret != 1)
|
||||||
|
state->type = ARCHIVE_TRANSFER_DEINIT;
|
||||||
|
if (ret == -1)
|
||||||
|
state->type = ARCHIVE_TRANSFER_DEINIT_ERROR;
|
||||||
|
|
||||||
|
/* early return to prevent deinit from never firing */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
case ARCHIVE_TRANSFER_DEINIT_ERROR:
|
||||||
|
*returnerr = false;
|
||||||
|
case ARCHIVE_TRANSFER_DEINIT:
|
||||||
|
if (state->handle)
|
||||||
|
{
|
||||||
|
file_archive_free(state->handle);
|
||||||
|
state->handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->stream && state->backend)
|
||||||
|
{
|
||||||
|
if (state->backend->stream_free)
|
||||||
|
state->backend->stream_free(state->stream);
|
||||||
|
|
||||||
|
if (state->stream)
|
||||||
|
free(state->stream);
|
||||||
|
|
||||||
|
state->stream = NULL;
|
||||||
|
|
||||||
|
if (userdata)
|
||||||
|
userdata->context = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( state->type == ARCHIVE_TRANSFER_DEINIT ||
|
||||||
|
state->type == ARCHIVE_TRANSFER_DEINIT_ERROR)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* file_archive_walk:
|
||||||
|
* @file : filename path of archive
|
||||||
|
* @valid_exts : Valid extensions of archive to be parsed.
|
||||||
|
* If NULL, allow all.
|
||||||
|
* @file_cb : file_cb function pointer
|
||||||
|
* @userdata : userdata to pass to file_cb function pointer.
|
||||||
|
*
|
||||||
|
* Low-level file parsing. Enumerates over all files and calls
|
||||||
|
* file_cb with userdata.
|
||||||
|
*
|
||||||
|
* Returns: true (1) on success, otherwise false (0).
|
||||||
|
**/
|
||||||
|
static bool file_archive_walk(const char *file, const char *valid_exts,
|
||||||
|
file_archive_file_cb file_cb, struct archive_extract_userdata *userdata)
|
||||||
|
{
|
||||||
|
file_archive_transfer_t state;
|
||||||
|
bool returnerr = true;
|
||||||
|
|
||||||
|
state.type = ARCHIVE_TRANSFER_INIT;
|
||||||
|
state.archive_size = 0;
|
||||||
|
state.handle = NULL;
|
||||||
|
state.stream = NULL;
|
||||||
|
state.footer = NULL;
|
||||||
|
state.directory = NULL;
|
||||||
|
state.data = NULL;
|
||||||
|
state.backend = NULL;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (file_archive_parse_file_iterate(&state, &returnerr, file,
|
||||||
|
valid_exts, file_cb, userdata) != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_archive_parse_file_progress(file_archive_transfer_t *state)
|
||||||
|
{
|
||||||
|
/* FIXME: this estimate is worse than before */
|
||||||
|
ptrdiff_t delta = 0;
|
||||||
|
|
||||||
|
if (!state || state->archive_size == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
delta = state->directory - state->data;
|
||||||
|
|
||||||
|
return (int)(delta * 100 / state->archive_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* file_archive_extract_file:
|
||||||
|
* @archive_path : filename path to archive.
|
||||||
|
* @archive_path_size : size of archive.
|
||||||
|
* @valid_exts : valid extensions for the file.
|
||||||
|
* @extraction_directory : the directory to extract temporary
|
||||||
|
* file to.
|
||||||
|
*
|
||||||
|
* Extract file from archive. If no file inside the archive is
|
||||||
|
* specified, the first file found will be used.
|
||||||
|
*
|
||||||
|
* Returns : true (1) on success, otherwise false (0).
|
||||||
|
**/
|
||||||
|
bool file_archive_extract_file(
|
||||||
|
char *archive_path,
|
||||||
|
size_t archive_path_size,
|
||||||
|
const char *valid_exts,
|
||||||
|
const char *extraction_directory,
|
||||||
|
char *out_path, size_t len)
|
||||||
|
{
|
||||||
|
struct archive_extract_userdata userdata;
|
||||||
|
bool ret = true;
|
||||||
|
struct string_list *list = string_split(valid_exts, "|");
|
||||||
|
|
||||||
|
userdata.archive_path[0] = '\0';
|
||||||
|
userdata.first_extracted_file_path = NULL;
|
||||||
|
userdata.extracted_file_path = NULL;
|
||||||
|
userdata.extraction_directory = extraction_directory;
|
||||||
|
userdata.archive_path_size = archive_path_size;
|
||||||
|
userdata.ext = list;
|
||||||
|
userdata.list = NULL;
|
||||||
|
userdata.found_file = false;
|
||||||
|
userdata.list_only = false;
|
||||||
|
userdata.context = NULL;
|
||||||
|
userdata.archive_name[0] = '\0';
|
||||||
|
userdata.crc = 0;
|
||||||
|
userdata.dec = NULL;
|
||||||
|
|
||||||
|
userdata.decomp_state.opt_file = NULL;
|
||||||
|
userdata.decomp_state.needle = NULL;
|
||||||
|
userdata.decomp_state.size = 0;
|
||||||
|
userdata.decomp_state.found = false;
|
||||||
|
|
||||||
|
if (!list)
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_archive_walk(archive_path, valid_exts,
|
||||||
|
file_archive_extract_cb, &userdata))
|
||||||
|
{
|
||||||
|
/* Parsing file archive failed. */
|
||||||
|
ret = false;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userdata.found_file)
|
||||||
|
{
|
||||||
|
/* Didn't find any file that matched valid extensions
|
||||||
|
* for libretro implementation. */
|
||||||
|
ret = false;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string_is_empty(userdata.first_extracted_file_path))
|
||||||
|
strlcpy(out_path, userdata.first_extracted_file_path, len);
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (userdata.first_extracted_file_path)
|
||||||
|
free(userdata.first_extracted_file_path);
|
||||||
|
if (list)
|
||||||
|
string_list_free(list);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* file_archive_get_file_list:
|
||||||
|
* @path : filename path of archive
|
||||||
|
*
|
||||||
|
* Returns: string listing of files from archive on success, otherwise NULL.
|
||||||
|
**/
|
||||||
|
struct string_list *file_archive_get_file_list(const char *path,
|
||||||
|
const char *valid_exts)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct archive_extract_userdata userdata;
|
||||||
|
|
||||||
|
strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path));
|
||||||
|
userdata.first_extracted_file_path = NULL;
|
||||||
|
userdata.extracted_file_path = NULL;
|
||||||
|
userdata.extraction_directory = NULL;
|
||||||
|
userdata.archive_path_size = 0;
|
||||||
|
userdata.ext = NULL;
|
||||||
|
userdata.list = string_list_new();
|
||||||
|
userdata.found_file = false;
|
||||||
|
userdata.list_only = true;
|
||||||
|
userdata.context = NULL;
|
||||||
|
userdata.archive_name[0] = '\0';
|
||||||
|
userdata.crc = 0;
|
||||||
|
userdata.dec = NULL;
|
||||||
|
|
||||||
|
userdata.decomp_state.opt_file = NULL;
|
||||||
|
userdata.decomp_state.needle = NULL;
|
||||||
|
userdata.decomp_state.size = 0;
|
||||||
|
userdata.decomp_state.found = false;
|
||||||
|
|
||||||
|
if (!userdata.list)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = file_archive_walk(path, valid_exts,
|
||||||
|
file_archive_get_file_list_cb, &userdata);
|
||||||
|
|
||||||
|
if (ret <= 0)
|
||||||
|
{
|
||||||
|
if (ret != -1)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userdata.list;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (userdata.list)
|
||||||
|
string_list_free(userdata.list);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file_archive_perform_mode(const char *path, const char *valid_exts,
|
||||||
|
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
|
||||||
|
uint32_t crc32, struct archive_extract_userdata *userdata)
|
||||||
|
{
|
||||||
|
switch (cmode)
|
||||||
|
{
|
||||||
|
case ARCHIVE_MODE_UNCOMPRESSED:
|
||||||
|
if (!filestream_write_file(path, cdata, size))
|
||||||
|
goto error;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARCHIVE_MODE_COMPRESSED:
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
file_archive_file_handle_t handle;
|
||||||
|
|
||||||
|
handle.stream = userdata->context;
|
||||||
|
handle.data = NULL;
|
||||||
|
handle.real_checksum = 0;
|
||||||
|
handle.backend = file_archive_get_file_backend(userdata->archive_path);
|
||||||
|
|
||||||
|
if (!handle.backend)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!handle.backend->stream_decompress_data_to_file_init(&handle,
|
||||||
|
cdata, csize, size))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = handle.backend->stream_decompress_data_to_file_iterate(
|
||||||
|
handle.stream);
|
||||||
|
}while(ret == 0);
|
||||||
|
|
||||||
|
if (!file_archive_decompress_data_to_file(&handle,
|
||||||
|
ret, path, valid_exts,
|
||||||
|
cdata, csize, size, crc32))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* file_archive_filename_split:
|
||||||
|
* @str : filename to turn into a string list
|
||||||
|
*
|
||||||
|
* Creates a new string list based on filename @path, delimited by a hash (#).
|
||||||
|
*
|
||||||
|
* Returns: new string list if successful, otherwise NULL.
|
||||||
|
*/
|
||||||
|
static struct string_list *file_archive_filename_split(const char *path)
|
||||||
|
{
|
||||||
|
union string_list_elem_attr attr;
|
||||||
|
struct string_list *list = string_list_new();
|
||||||
|
const char *delim = path_get_archive_delim(path);
|
||||||
|
|
||||||
|
attr.i = 0;
|
||||||
|
|
||||||
|
if (delim)
|
||||||
|
{
|
||||||
|
/* add archive path to list first */
|
||||||
|
if (!string_list_append_n(list, path, (unsigned)(delim - path), attr))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* now add the path within the archive */
|
||||||
|
delim++;
|
||||||
|
|
||||||
|
if (*delim)
|
||||||
|
{
|
||||||
|
if (!string_list_append(list, delim, attr))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (!string_list_append(list, path, attr))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return list;
|
||||||
|
|
||||||
|
error:
|
||||||
|
string_list_free(list);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic compressed file loader.
|
||||||
|
* Extracts to buf, unless optional_filename != 0
|
||||||
|
* Then extracts to optional_filename and leaves buf alone.
|
||||||
|
*/
|
||||||
|
int file_archive_compressed_read(
|
||||||
|
const char * path, void **buf,
|
||||||
|
const char* optional_filename, ssize_t *length)
|
||||||
|
{
|
||||||
|
const struct file_archive_file_backend *backend = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
struct string_list *str_list = file_archive_filename_split(path);
|
||||||
|
|
||||||
|
/* Safety check.
|
||||||
|
* If optional_filename and optional_filename
|
||||||
|
* exists, we simply return 0,
|
||||||
|
* hoping that optional_filename is the
|
||||||
|
* same as requested.
|
||||||
|
*/
|
||||||
|
if (optional_filename && filestream_exists(optional_filename))
|
||||||
|
{
|
||||||
|
*length = 0;
|
||||||
|
string_list_free(str_list);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We assure that there is something after the '#' symbol.
|
||||||
|
*
|
||||||
|
* This error condition happens for example, when
|
||||||
|
* path = /path/to/file.7z, or
|
||||||
|
* path = /path/to/file.7z#
|
||||||
|
*/
|
||||||
|
if (str_list->size <= 1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
backend = file_archive_get_file_backend(str_list->elems[0].data);
|
||||||
|
|
||||||
|
*length = backend->compressed_file_read(str_list->elems[0].data,
|
||||||
|
str_list->elems[1].data, buf, optional_filename);
|
||||||
|
|
||||||
|
if (*length != -1)
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
string_list_free(str_list);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
error:
|
||||||
|
/* could not extract string and substring. */
|
||||||
|
string_list_free(str_list);
|
||||||
|
*length = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct file_archive_file_backend *file_archive_get_zlib_file_backend(void)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
return &zlib_backend;
|
||||||
|
#else
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct file_archive_file_backend *file_archive_get_7z_file_backend(void)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_7ZIP
|
||||||
|
return &sevenzip_backend;
|
||||||
|
#else
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct file_archive_file_backend* file_archive_get_file_backend(const char *path)
|
||||||
|
{
|
||||||
|
char newpath[PATH_MAX_LENGTH];
|
||||||
|
const char *file_ext = NULL;
|
||||||
|
char *last = NULL;
|
||||||
|
|
||||||
|
newpath[0] = '\0';
|
||||||
|
|
||||||
|
strlcpy(newpath, path, sizeof(newpath));
|
||||||
|
|
||||||
|
last = (char*)path_get_archive_delim(newpath);
|
||||||
|
|
||||||
|
if (last)
|
||||||
|
*last = '\0';
|
||||||
|
|
||||||
|
file_ext = path_get_extension(newpath);
|
||||||
|
|
||||||
|
#ifdef HAVE_7ZIP
|
||||||
|
if (string_is_equal_noncase(file_ext, "7z"))
|
||||||
|
return &sevenzip_backend;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
if ( string_is_equal_noncase(file_ext, "zip")
|
||||||
|
|| string_is_equal_noncase(file_ext, "apk")
|
||||||
|
)
|
||||||
|
return &zlib_backend;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* file_archive_get_file_crc32:
|
||||||
|
* @path : filename path of archive
|
||||||
|
*
|
||||||
|
* Returns: CRC32 of the specified file in the archive, otherwise 0.
|
||||||
|
* If no path within the archive is specified, the first
|
||||||
|
* file found inside is used.
|
||||||
|
**/
|
||||||
|
uint32_t file_archive_get_file_crc32(const char *path)
|
||||||
|
{
|
||||||
|
file_archive_transfer_t state;
|
||||||
|
const struct file_archive_file_backend *backend = file_archive_get_file_backend(path);
|
||||||
|
struct archive_extract_userdata userdata = {{0}};
|
||||||
|
bool returnerr = false;
|
||||||
|
bool contains_compressed = false;
|
||||||
|
const char *archive_path = NULL;
|
||||||
|
|
||||||
|
if (!backend)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
contains_compressed = path_contains_compressed_file(path);
|
||||||
|
|
||||||
|
if (contains_compressed)
|
||||||
|
{
|
||||||
|
archive_path = path_get_archive_delim(path);
|
||||||
|
|
||||||
|
/* move pointer right after the delimiter to give us the path */
|
||||||
|
if (archive_path)
|
||||||
|
archive_path += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.type = ARCHIVE_TRANSFER_INIT;
|
||||||
|
state.archive_size = 0;
|
||||||
|
state.handle = NULL;
|
||||||
|
state.stream = NULL;
|
||||||
|
state.footer = NULL;
|
||||||
|
state.directory = NULL;
|
||||||
|
state.data = NULL;
|
||||||
|
state.backend = NULL;
|
||||||
|
|
||||||
|
/* Initialize and open archive first.
|
||||||
|
Sets next state type to ITERATE. */
|
||||||
|
file_archive_parse_file_iterate(&state,
|
||||||
|
&returnerr, path, NULL, NULL,
|
||||||
|
&userdata);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
/* Now find the first file in the archive. */
|
||||||
|
if (state.type == ARCHIVE_TRANSFER_ITERATE)
|
||||||
|
file_archive_parse_file_iterate(&state,
|
||||||
|
&returnerr, path, NULL, NULL,
|
||||||
|
&userdata);
|
||||||
|
|
||||||
|
/* If no path specified within archive, stop after
|
||||||
|
* finding the first file.
|
||||||
|
*/
|
||||||
|
if (!contains_compressed)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Stop when the right file in the archive is found. */
|
||||||
|
if (archive_path)
|
||||||
|
{
|
||||||
|
if (string_is_equal(userdata.extracted_file_path, archive_path))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_archive_parse_file_iterate_stop(&state);
|
||||||
|
|
||||||
|
if (userdata.crc)
|
||||||
|
return userdata.crc;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,512 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (archive_file_sevenzip.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
|
||||||
|
#include <boolean.h>
|
||||||
|
#include <file/archive_file.h>
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <encodings/utf.h>
|
||||||
|
#include <encodings/crc32.h>
|
||||||
|
#include <string/stdstring.h>
|
||||||
|
#include <lists/string_list.h>
|
||||||
|
#include <file/file_path.h>
|
||||||
|
#include <compat/strl.h>
|
||||||
|
#include <7zip/7z.h>
|
||||||
|
#include <7zip/7zCrc.h>
|
||||||
|
#include <7zip/7zFile.h>
|
||||||
|
|
||||||
|
#define SEVENZIP_MAGIC "7z\xBC\xAF\x27\x1C"
|
||||||
|
#define SEVENZIP_MAGIC_LEN 6
|
||||||
|
|
||||||
|
/* Assume W-functions do not work below Win2K and Xbox platforms */
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||||
|
#ifndef LEGACY_WIN32
|
||||||
|
#define LEGACY_WIN32
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct sevenzip_context_t {
|
||||||
|
CFileInStream archiveStream;
|
||||||
|
CLookToRead lookStream;
|
||||||
|
ISzAlloc allocImp;
|
||||||
|
ISzAlloc allocTempImp;
|
||||||
|
CSzArEx db;
|
||||||
|
size_t temp_size;
|
||||||
|
uint32_t block_index;
|
||||||
|
uint32_t index;
|
||||||
|
uint32_t packIndex;
|
||||||
|
uint8_t *output;
|
||||||
|
file_archive_file_handle_t *handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *sevenzip_stream_alloc_impl(void *p, size_t size)
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
return 0;
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sevenzip_stream_free_impl(void *p, void *address)
|
||||||
|
{
|
||||||
|
(void)p;
|
||||||
|
free(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *sevenzip_stream_alloc_tmp_impl(void *p, size_t size)
|
||||||
|
{
|
||||||
|
(void)p;
|
||||||
|
if (size == 0)
|
||||||
|
return 0;
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* sevenzip_stream_new(void)
|
||||||
|
{
|
||||||
|
struct sevenzip_context_t *sevenzip_context =
|
||||||
|
(struct sevenzip_context_t*)calloc(1, sizeof(struct sevenzip_context_t));
|
||||||
|
|
||||||
|
/* These are the allocation routines - currently using
|
||||||
|
* the non-standard 7zip choices. */
|
||||||
|
sevenzip_context->allocImp.Alloc = sevenzip_stream_alloc_impl;
|
||||||
|
sevenzip_context->allocImp.Free = sevenzip_stream_free_impl;
|
||||||
|
sevenzip_context->allocTempImp.Alloc = sevenzip_stream_alloc_tmp_impl;
|
||||||
|
sevenzip_context->allocTempImp.Free = sevenzip_stream_free_impl;
|
||||||
|
sevenzip_context->block_index = 0xFFFFFFFF;
|
||||||
|
sevenzip_context->output = NULL;
|
||||||
|
sevenzip_context->handle = NULL;
|
||||||
|
|
||||||
|
return sevenzip_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sevenzip_stream_free(void *data)
|
||||||
|
{
|
||||||
|
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)data;
|
||||||
|
|
||||||
|
if (!sevenzip_context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sevenzip_context->output)
|
||||||
|
{
|
||||||
|
IAlloc_Free(&sevenzip_context->allocImp, sevenzip_context->output);
|
||||||
|
sevenzip_context->output = NULL;
|
||||||
|
sevenzip_context->handle->data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp);
|
||||||
|
File_Close(&sevenzip_context->archiveStream.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract the relative path (needle) from a 7z archive
|
||||||
|
* (path) and allocate a buf for it to write it in.
|
||||||
|
* If optional_outfile is set, extract to that instead
|
||||||
|
* and don't allocate buffer.
|
||||||
|
*/
|
||||||
|
static int sevenzip_file_read(
|
||||||
|
const char *path,
|
||||||
|
const char *needle, void **buf,
|
||||||
|
const char *optional_outfile)
|
||||||
|
{
|
||||||
|
CFileInStream archiveStream;
|
||||||
|
CLookToRead lookStream;
|
||||||
|
ISzAlloc allocImp;
|
||||||
|
ISzAlloc allocTempImp;
|
||||||
|
CSzArEx db;
|
||||||
|
uint8_t *output = 0;
|
||||||
|
long outsize = -1;
|
||||||
|
|
||||||
|
/*These are the allocation routines.
|
||||||
|
* Currently using the non-standard 7zip choices. */
|
||||||
|
allocImp.Alloc = sevenzip_stream_alloc_impl;
|
||||||
|
allocImp.Free = sevenzip_stream_free_impl;
|
||||||
|
allocTempImp.Alloc = sevenzip_stream_alloc_tmp_impl;
|
||||||
|
allocTempImp.Free = sevenzip_stream_free_impl;
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32)
|
||||||
|
if (!string_is_empty(path))
|
||||||
|
{
|
||||||
|
wchar_t *pathW = utf8_to_utf16_string_alloc(path);
|
||||||
|
|
||||||
|
if (pathW)
|
||||||
|
{
|
||||||
|
/* Could not open 7zip archive? */
|
||||||
|
if (InFile_OpenW(&archiveStream.file, pathW))
|
||||||
|
{
|
||||||
|
free(pathW);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pathW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Could not open 7zip archive? */
|
||||||
|
if (InFile_Open(&archiveStream.file, path))
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FileInStream_CreateVTable(&archiveStream);
|
||||||
|
LookToRead_CreateVTable(&lookStream, false);
|
||||||
|
lookStream.realStream = &archiveStream.s;
|
||||||
|
LookToRead_Init(&lookStream);
|
||||||
|
CrcGenerateTable();
|
||||||
|
|
||||||
|
db.db.PackSizes = NULL;
|
||||||
|
db.db.PackCRCsDefined = NULL;
|
||||||
|
db.db.PackCRCs = NULL;
|
||||||
|
db.db.Folders = NULL;
|
||||||
|
db.db.Files = NULL;
|
||||||
|
db.db.NumPackStreams = 0;
|
||||||
|
db.db.NumFolders = 0;
|
||||||
|
db.db.NumFiles = 0;
|
||||||
|
db.startPosAfterHeader = 0;
|
||||||
|
db.dataPos = 0;
|
||||||
|
db.FolderStartPackStreamIndex = NULL;
|
||||||
|
db.PackStreamStartPositions = NULL;
|
||||||
|
db.FolderStartFileIndex = NULL;
|
||||||
|
db.FileIndexToFolderIndexMap = NULL;
|
||||||
|
db.FileNameOffsets = NULL;
|
||||||
|
db.FileNames.data = NULL;
|
||||||
|
db.FileNames.size = 0;
|
||||||
|
|
||||||
|
SzArEx_Init(&db);
|
||||||
|
|
||||||
|
if (SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp) == SZ_OK)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
bool file_found = false;
|
||||||
|
uint16_t *temp = NULL;
|
||||||
|
size_t temp_size = 0;
|
||||||
|
uint32_t block_index = 0xFFFFFFFF;
|
||||||
|
SRes res = SZ_OK;
|
||||||
|
|
||||||
|
for (i = 0; i < db.db.NumFiles; i++)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char infile[PATH_MAX_LENGTH];
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t outSizeProcessed = 0;
|
||||||
|
const CSzFileItem *f = db.db.Files + i;
|
||||||
|
|
||||||
|
/* We skip over everything which is not a directory.
|
||||||
|
* FIXME: Why continue then if f->IsDir is true?*/
|
||||||
|
if (f->IsDir)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
len = SzArEx_GetFileNameUtf16(&db, i, NULL);
|
||||||
|
|
||||||
|
if (len > temp_size)
|
||||||
|
{
|
||||||
|
if (temp)
|
||||||
|
free(temp);
|
||||||
|
temp_size = len;
|
||||||
|
temp = (uint16_t *)malloc(temp_size * sizeof(temp[0]));
|
||||||
|
|
||||||
|
if (temp == 0)
|
||||||
|
{
|
||||||
|
res = SZ_ERROR_MEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SzArEx_GetFileNameUtf16(&db, i, temp);
|
||||||
|
res = SZ_ERROR_FAIL;
|
||||||
|
infile[0] = '\0';
|
||||||
|
|
||||||
|
if (temp)
|
||||||
|
res = utf16_to_char_string(temp, infile, sizeof(infile))
|
||||||
|
? SZ_OK : SZ_ERROR_FAIL;
|
||||||
|
|
||||||
|
if (string_is_equal(infile, needle))
|
||||||
|
{
|
||||||
|
size_t output_size = 0;
|
||||||
|
|
||||||
|
/* C LZMA SDK does not support chunked extraction - see here:
|
||||||
|
* sourceforge.net/p/sevenzip/discussion/45798/thread/6fb59aaf/
|
||||||
|
* */
|
||||||
|
file_found = true;
|
||||||
|
res = SzArEx_Extract(&db, &lookStream.s, i, &block_index,
|
||||||
|
&output, &output_size, &offset, &outSizeProcessed,
|
||||||
|
&allocImp, &allocTempImp);
|
||||||
|
|
||||||
|
if (res != SZ_OK)
|
||||||
|
break; /* This goes to the error section. */
|
||||||
|
|
||||||
|
outsize = outSizeProcessed;
|
||||||
|
|
||||||
|
if (optional_outfile != NULL)
|
||||||
|
{
|
||||||
|
const void *ptr = (const void*)(output + offset);
|
||||||
|
|
||||||
|
if (!filestream_write_file(optional_outfile, ptr, outsize))
|
||||||
|
{
|
||||||
|
res = SZ_OK;
|
||||||
|
file_found = true;
|
||||||
|
outsize = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*We could either use the 7Zip allocated buffer,
|
||||||
|
* or create our own and use it.
|
||||||
|
* We would however need to realloc anyways, because RetroArch
|
||||||
|
* expects a \0 at the end, therefore we allocate new,
|
||||||
|
* copy and free the old one. */
|
||||||
|
*buf = malloc(outsize + 1);
|
||||||
|
((char*)(*buf))[outsize] = '\0';
|
||||||
|
memcpy(*buf,output + offset,outsize);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temp)
|
||||||
|
free(temp);
|
||||||
|
IAlloc_Free(&allocImp, output);
|
||||||
|
|
||||||
|
if (!(file_found && res == SZ_OK))
|
||||||
|
{
|
||||||
|
/* Error handling
|
||||||
|
*
|
||||||
|
* Failed to open compressed file inside 7zip archive.
|
||||||
|
*/
|
||||||
|
|
||||||
|
outsize = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SzArEx_Free(&db, &allocImp);
|
||||||
|
File_Close(&archiveStream.file);
|
||||||
|
|
||||||
|
return (int)outsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sevenzip_stream_decompress_data_to_file_init(
|
||||||
|
file_archive_file_handle_t *handle,
|
||||||
|
const uint8_t *cdata, uint32_t csize, uint32_t size)
|
||||||
|
{
|
||||||
|
struct sevenzip_context_t *sevenzip_context =
|
||||||
|
(struct sevenzip_context_t*)handle->stream;
|
||||||
|
|
||||||
|
if (!sevenzip_context)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sevenzip_context->handle = handle;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
|
||||||
|
{
|
||||||
|
struct sevenzip_context_t *sevenzip_context =
|
||||||
|
(struct sevenzip_context_t*)data;
|
||||||
|
|
||||||
|
SRes res = SZ_ERROR_FAIL;
|
||||||
|
size_t output_size = 0;
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t outSizeProcessed = 0;
|
||||||
|
|
||||||
|
res = SzArEx_Extract(&sevenzip_context->db,
|
||||||
|
&sevenzip_context->lookStream.s, sevenzip_context->index,
|
||||||
|
&sevenzip_context->block_index, &sevenzip_context->output,
|
||||||
|
&output_size, &offset, &outSizeProcessed,
|
||||||
|
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp);
|
||||||
|
|
||||||
|
if (res != SZ_OK)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (sevenzip_context->handle)
|
||||||
|
sevenzip_context->handle->data = sevenzip_context->output + offset;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sevenzip_parse_file_init(file_archive_transfer_t *state,
|
||||||
|
const char *file)
|
||||||
|
{
|
||||||
|
struct sevenzip_context_t *sevenzip_context =
|
||||||
|
(struct sevenzip_context_t*)sevenzip_stream_new();
|
||||||
|
|
||||||
|
if (state->archive_size < SEVENZIP_MAGIC_LEN)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (string_is_not_equal_fast(state->data, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
state->stream = sevenzip_context;
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32)
|
||||||
|
if (!string_is_empty(file))
|
||||||
|
{
|
||||||
|
wchar_t *fileW = utf8_to_utf16_string_alloc(file);
|
||||||
|
|
||||||
|
if (fileW)
|
||||||
|
{
|
||||||
|
/* could not open 7zip archive? */
|
||||||
|
if (InFile_OpenW(&sevenzip_context->archiveStream.file, fileW))
|
||||||
|
{
|
||||||
|
free(fileW);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(fileW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* could not open 7zip archive? */
|
||||||
|
if (InFile_Open(&sevenzip_context->archiveStream.file, file))
|
||||||
|
goto error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FileInStream_CreateVTable(&sevenzip_context->archiveStream);
|
||||||
|
LookToRead_CreateVTable(&sevenzip_context->lookStream, false);
|
||||||
|
sevenzip_context->lookStream.realStream = &sevenzip_context->archiveStream.s;
|
||||||
|
LookToRead_Init(&sevenzip_context->lookStream);
|
||||||
|
CrcGenerateTable();
|
||||||
|
SzArEx_Init(&sevenzip_context->db);
|
||||||
|
|
||||||
|
if (SzArEx_Open(&sevenzip_context->db, &sevenzip_context->lookStream.s,
|
||||||
|
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp) != SZ_OK)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (sevenzip_context)
|
||||||
|
sevenzip_stream_free(sevenzip_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sevenzip_parse_file_iterate_step_internal(
|
||||||
|
file_archive_transfer_t *state, char *filename,
|
||||||
|
const uint8_t **cdata, unsigned *cmode,
|
||||||
|
uint32_t *size, uint32_t *csize, uint32_t *checksum,
|
||||||
|
unsigned *payback, struct archive_extract_userdata *userdata)
|
||||||
|
{
|
||||||
|
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)state->stream;
|
||||||
|
const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->index;
|
||||||
|
|
||||||
|
if (sevenzip_context->index < sevenzip_context->db.db.NumFiles)
|
||||||
|
{
|
||||||
|
size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db,
|
||||||
|
sevenzip_context->index, NULL);
|
||||||
|
uint64_t compressed_size = 0;
|
||||||
|
|
||||||
|
if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams)
|
||||||
|
{
|
||||||
|
compressed_size = sevenzip_context->db.db.PackSizes[sevenzip_context->packIndex];
|
||||||
|
sevenzip_context->packIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < PATH_MAX_LENGTH && !file->IsDir)
|
||||||
|
{
|
||||||
|
char infile[PATH_MAX_LENGTH];
|
||||||
|
SRes res = SZ_ERROR_FAIL;
|
||||||
|
uint16_t *temp = (uint16_t*)malloc(len * sizeof(uint16_t));
|
||||||
|
|
||||||
|
if (!temp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
infile[0] = '\0';
|
||||||
|
|
||||||
|
SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index,
|
||||||
|
temp);
|
||||||
|
|
||||||
|
if (temp)
|
||||||
|
{
|
||||||
|
res = utf16_to_char_string(temp, infile, sizeof(infile))
|
||||||
|
? SZ_OK : SZ_ERROR_FAIL;
|
||||||
|
free(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res != SZ_OK)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
strlcpy(filename, infile, PATH_MAX_LENGTH);
|
||||||
|
|
||||||
|
*cmode = ARCHIVE_MODE_COMPRESSED;
|
||||||
|
*checksum = file->Crc;
|
||||||
|
*size = (uint32_t)file->Size;
|
||||||
|
*csize = (uint32_t)compressed_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*payback = 1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state,
|
||||||
|
const char *valid_exts,
|
||||||
|
struct archive_extract_userdata *userdata, file_archive_file_cb file_cb)
|
||||||
|
{
|
||||||
|
char filename[PATH_MAX_LENGTH];
|
||||||
|
const uint8_t *cdata = NULL;
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
uint32_t csize = 0;
|
||||||
|
unsigned cmode = 0;
|
||||||
|
unsigned payload = 0;
|
||||||
|
struct sevenzip_context_t *sevenzip_context = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
filename[0] = '\0';
|
||||||
|
|
||||||
|
ret = sevenzip_parse_file_iterate_step_internal(state, filename,
|
||||||
|
&cdata, &cmode, &size, &csize,
|
||||||
|
&checksum, &payload, userdata);
|
||||||
|
|
||||||
|
if (ret != 1)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
userdata->extracted_file_path = filename;
|
||||||
|
userdata->crc = checksum;
|
||||||
|
|
||||||
|
if (file_cb && !file_cb(filename, valid_exts, cdata, cmode,
|
||||||
|
csize, size, checksum, userdata))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sevenzip_context = (struct sevenzip_context_t*)state->stream;
|
||||||
|
|
||||||
|
sevenzip_context->index += payload;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sevenzip_stream_crc32_calculate(uint32_t crc,
|
||||||
|
const uint8_t *data, size_t length)
|
||||||
|
{
|
||||||
|
return encoding_crc32(crc, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct file_archive_file_backend sevenzip_backend = {
|
||||||
|
sevenzip_stream_new,
|
||||||
|
sevenzip_stream_free,
|
||||||
|
sevenzip_stream_decompress_data_to_file_init,
|
||||||
|
sevenzip_stream_decompress_data_to_file_iterate,
|
||||||
|
sevenzip_stream_crc32_calculate,
|
||||||
|
sevenzip_file_read,
|
||||||
|
sevenzip_parse_file_init,
|
||||||
|
sevenzip_parse_file_iterate_step,
|
||||||
|
"7z"
|
||||||
|
};
|
|
@ -0,0 +1,384 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (archive_file_zlib.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <file/archive_file.h>
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <streams/trans_stream.h>
|
||||||
|
#include <retro_inline.h>
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
#include <encodings/crc32.h>
|
||||||
|
|
||||||
|
/* Only for MAX_WBITS */
|
||||||
|
#include <compat/zlib.h>
|
||||||
|
|
||||||
|
#ifndef CENTRAL_FILE_HEADER_SIGNATURE
|
||||||
|
#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef END_OF_CENTRAL_DIR_SIGNATURE
|
||||||
|
#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
uint32_t val = 0;
|
||||||
|
|
||||||
|
size *= 8;
|
||||||
|
for (i = 0; i < size; i += 8)
|
||||||
|
val |= (uint32_t)*data++ << i;
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *zlib_stream_new(void)
|
||||||
|
{
|
||||||
|
return zlib_inflate_backend.stream_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zlib_stream_free(void *stream)
|
||||||
|
{
|
||||||
|
zlib_inflate_backend.stream_free(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool zlib_stream_decompress_data_to_file_init(
|
||||||
|
file_archive_file_handle_t *handle,
|
||||||
|
const uint8_t *cdata, uint32_t csize, uint32_t size)
|
||||||
|
{
|
||||||
|
if (!handle)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
handle->stream = zlib_inflate_backend.stream_new();
|
||||||
|
|
||||||
|
if (!handle->stream)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (zlib_inflate_backend.define)
|
||||||
|
zlib_inflate_backend.define(handle->stream, "window_bits", (uint32_t)-MAX_WBITS);
|
||||||
|
|
||||||
|
handle->data = (uint8_t*)malloc(size);
|
||||||
|
|
||||||
|
if (!handle->data)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
zlib_inflate_backend.set_in(handle->stream,
|
||||||
|
(const uint8_t*)cdata, csize);
|
||||||
|
zlib_inflate_backend.set_out(handle->stream,
|
||||||
|
handle->data, size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (handle->stream)
|
||||||
|
zlib_inflate_backend.stream_free(handle->stream);
|
||||||
|
if (handle->data)
|
||||||
|
free(handle->data);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zlib_stream_decompress_data_to_file_iterate(void *stream)
|
||||||
|
{
|
||||||
|
bool zstatus;
|
||||||
|
uint32_t rd, wn;
|
||||||
|
enum trans_stream_error terror;
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
zstatus = zlib_inflate_backend.trans(stream, false, &rd, &wn, &terror);
|
||||||
|
|
||||||
|
if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (zstatus && !terror)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t zlib_stream_crc32_calculate(uint32_t crc,
|
||||||
|
const uint8_t *data, size_t length)
|
||||||
|
{
|
||||||
|
return encoding_crc32(crc, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool zip_file_decompressed_handle(
|
||||||
|
file_archive_file_handle_t *handle,
|
||||||
|
const uint8_t *cdata, uint32_t csize,
|
||||||
|
uint32_t size, uint32_t crc32)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
handle->backend = &zlib_backend;
|
||||||
|
|
||||||
|
if (!handle->backend->stream_decompress_data_to_file_init(
|
||||||
|
handle, cdata, csize, size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = handle->backend->stream_decompress_data_to_file_iterate(
|
||||||
|
handle->stream);
|
||||||
|
}while(ret == 0);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
handle->real_checksum = handle->backend->stream_crc_calculate(0,
|
||||||
|
handle->data, size);
|
||||||
|
|
||||||
|
if (handle->real_checksum != crc32)
|
||||||
|
goto error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (handle->stream)
|
||||||
|
free(handle->stream);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#if 0
|
||||||
|
error:
|
||||||
|
if (handle->stream)
|
||||||
|
free(handle->stream);
|
||||||
|
if (handle->data)
|
||||||
|
free(handle->data);
|
||||||
|
|
||||||
|
handle->stream = NULL;
|
||||||
|
handle->data = NULL;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract the relative path (needle) from a
|
||||||
|
* ZIP archive (path) and allocate a buffer for it to write it in.
|
||||||
|
*
|
||||||
|
* optional_outfile if not NULL will be used to extract the file to.
|
||||||
|
* buf will be 0 then.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int zip_file_decompressed(
|
||||||
|
const char *name, const char *valid_exts,
|
||||||
|
const uint8_t *cdata, unsigned cmode,
|
||||||
|
uint32_t csize, uint32_t size,
|
||||||
|
uint32_t crc32, struct archive_extract_userdata *userdata)
|
||||||
|
{
|
||||||
|
/* Ignore directories. */
|
||||||
|
if (name[strlen(name) - 1] == '/' || name[strlen(name) - 1] == '\\')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (strstr(name, userdata->decomp_state.needle))
|
||||||
|
{
|
||||||
|
bool goto_error = false;
|
||||||
|
file_archive_file_handle_t handle = {0};
|
||||||
|
|
||||||
|
userdata->decomp_state.found = true;
|
||||||
|
|
||||||
|
if (zip_file_decompressed_handle(&handle,
|
||||||
|
cdata, csize, size, crc32))
|
||||||
|
{
|
||||||
|
if (userdata->decomp_state.opt_file != 0)
|
||||||
|
{
|
||||||
|
/* Called in case core has need_fullpath enabled. */
|
||||||
|
char *buf = (char*)malloc(size);
|
||||||
|
|
||||||
|
if (buf)
|
||||||
|
{
|
||||||
|
memcpy(buf, handle.data, size);
|
||||||
|
|
||||||
|
if (!filestream_write_file(userdata->decomp_state.opt_file, buf, size))
|
||||||
|
goto_error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
userdata->decomp_state.size = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Called in case core has need_fullpath disabled.
|
||||||
|
* Will copy decompressed content directly into
|
||||||
|
* RetroArch's ROM buffer. */
|
||||||
|
*userdata->decomp_state.buf = malloc(size);
|
||||||
|
memcpy(*userdata->decomp_state.buf, handle.data, size);
|
||||||
|
|
||||||
|
userdata->decomp_state.size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.data)
|
||||||
|
free(handle.data);
|
||||||
|
|
||||||
|
if (goto_error)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zip_file_read(
|
||||||
|
const char *path,
|
||||||
|
const char *needle, void **buf,
|
||||||
|
const char *optional_outfile)
|
||||||
|
{
|
||||||
|
file_archive_transfer_t zlib;
|
||||||
|
struct archive_extract_userdata userdata = {{0}};
|
||||||
|
bool returnerr = true;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
zlib.type = ARCHIVE_TRANSFER_INIT;
|
||||||
|
|
||||||
|
userdata.decomp_state.needle = NULL;
|
||||||
|
userdata.decomp_state.opt_file = NULL;
|
||||||
|
userdata.decomp_state.found = false;
|
||||||
|
userdata.decomp_state.buf = buf;
|
||||||
|
|
||||||
|
if (needle)
|
||||||
|
userdata.decomp_state.needle = strdup(needle);
|
||||||
|
if (optional_outfile)
|
||||||
|
userdata.decomp_state.opt_file = strdup(optional_outfile);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = file_archive_parse_file_iterate(&zlib, &returnerr, path,
|
||||||
|
"", zip_file_decompressed, &userdata);
|
||||||
|
if (!returnerr)
|
||||||
|
break;
|
||||||
|
}while(ret == 0 && !userdata.decomp_state.found);
|
||||||
|
|
||||||
|
file_archive_parse_file_iterate_stop(&zlib);
|
||||||
|
|
||||||
|
if (userdata.decomp_state.opt_file)
|
||||||
|
free(userdata.decomp_state.opt_file);
|
||||||
|
if (userdata.decomp_state.needle)
|
||||||
|
free(userdata.decomp_state.needle);
|
||||||
|
|
||||||
|
if (!userdata.decomp_state.found)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return (int)userdata.decomp_state.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zip_parse_file_init(file_archive_transfer_t *state,
|
||||||
|
const char *file)
|
||||||
|
{
|
||||||
|
if (state->archive_size < 22)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
state->footer = state->data + state->archive_size - 22;
|
||||||
|
|
||||||
|
for (;; state->footer--)
|
||||||
|
{
|
||||||
|
if (state->footer <= state->data + 22)
|
||||||
|
return -1;
|
||||||
|
if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
|
||||||
|
{
|
||||||
|
unsigned comment_len = read_le(state->footer + 20, 2);
|
||||||
|
if (state->footer + 22 + comment_len == state->data + state->archive_size)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state->directory = state->data + read_le(state->footer + 16, 4);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zip_parse_file_iterate_step_internal(
|
||||||
|
file_archive_transfer_t *state, char *filename,
|
||||||
|
const uint8_t **cdata,
|
||||||
|
unsigned *cmode, uint32_t *size, uint32_t *csize,
|
||||||
|
uint32_t *checksum, unsigned *payback)
|
||||||
|
{
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t namelength, extralength, commentlength,
|
||||||
|
offsetNL, offsetEL;
|
||||||
|
uint32_t signature = read_le(state->directory + 0, 4);
|
||||||
|
|
||||||
|
if (signature != CENTRAL_FILE_HEADER_SIGNATURE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*cmode = read_le(state->directory + 10, 2); /* compression mode, 0 = store, 8 = deflate */
|
||||||
|
*checksum = read_le(state->directory + 16, 4); /* CRC32 */
|
||||||
|
*csize = read_le(state->directory + 20, 4); /* compressed size */
|
||||||
|
*size = read_le(state->directory + 24, 4); /* uncompressed size */
|
||||||
|
|
||||||
|
namelength = read_le(state->directory + 28, 2); /* file name length */
|
||||||
|
extralength = read_le(state->directory + 30, 2); /* extra field length */
|
||||||
|
commentlength = read_le(state->directory + 32, 2); /* file comment length */
|
||||||
|
|
||||||
|
if (namelength >= PATH_MAX_LENGTH)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memcpy(filename, state->directory + 46, namelength); /* file name */
|
||||||
|
|
||||||
|
offset = read_le(state->directory + 42, 4); /* relative offset of local file header */
|
||||||
|
offsetNL = read_le(state->data + offset + 26, 2); /* file name length */
|
||||||
|
offsetEL = read_le(state->data + offset + 28, 2); /* extra field length */
|
||||||
|
|
||||||
|
*cdata = state->data + offset + 30 + offsetNL + offsetEL;
|
||||||
|
|
||||||
|
*payback = 46 + namelength + extralength + commentlength;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zip_parse_file_iterate_step(file_archive_transfer_t *state,
|
||||||
|
const char *valid_exts, struct archive_extract_userdata *userdata,
|
||||||
|
file_archive_file_cb file_cb)
|
||||||
|
{
|
||||||
|
char filename[PATH_MAX_LENGTH] = {0};
|
||||||
|
const uint8_t *cdata = NULL;
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
uint32_t csize = 0;
|
||||||
|
unsigned cmode = 0;
|
||||||
|
unsigned payload = 0;
|
||||||
|
int ret = zip_parse_file_iterate_step_internal(
|
||||||
|
state, filename, &cdata, &cmode, &size, &csize, &checksum, &payload);
|
||||||
|
|
||||||
|
if (ret != 1)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
userdata->extracted_file_path = filename;
|
||||||
|
userdata->crc = checksum;
|
||||||
|
|
||||||
|
if (file_cb && !file_cb(filename, valid_exts, cdata, cmode,
|
||||||
|
csize, size, checksum, userdata))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
state->directory += payload;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct file_archive_file_backend zlib_backend = {
|
||||||
|
zlib_stream_new,
|
||||||
|
zlib_stream_free,
|
||||||
|
zlib_stream_decompress_data_to_file_init,
|
||||||
|
zlib_stream_decompress_data_to_file_iterate,
|
||||||
|
zlib_stream_crc32_calculate,
|
||||||
|
zip_file_read,
|
||||||
|
zip_parse_file_init,
|
||||||
|
zip_parse_file_iterate_step,
|
||||||
|
"zlib"
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,149 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (config_file_userdata.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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/file_path.h>
|
||||||
|
#include <lists/string_list.h>
|
||||||
|
|
||||||
|
#include <file/config_file_userdata.h>
|
||||||
|
|
||||||
|
int config_userdata_get_float(void *userdata, const char *key_str,
|
||||||
|
float *value, float default_value)
|
||||||
|
{
|
||||||
|
bool got;
|
||||||
|
char key[2][256];
|
||||||
|
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
|
||||||
|
|
||||||
|
fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0]));
|
||||||
|
fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1]));
|
||||||
|
|
||||||
|
got = config_get_float (usr->conf, key[0], value);
|
||||||
|
got = got || config_get_float(usr->conf, key[1], value);
|
||||||
|
|
||||||
|
if (!got)
|
||||||
|
*value = default_value;
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_userdata_get_int(void *userdata, const char *key_str,
|
||||||
|
int *value, int default_value)
|
||||||
|
{
|
||||||
|
bool got;
|
||||||
|
char key[2][256];
|
||||||
|
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
|
||||||
|
|
||||||
|
fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0]));
|
||||||
|
fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1]));
|
||||||
|
|
||||||
|
got = config_get_int (usr->conf, key[0], value);
|
||||||
|
got = got || config_get_int(usr->conf, key[1], value);
|
||||||
|
|
||||||
|
if (!got)
|
||||||
|
*value = default_value;
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_userdata_get_float_array(void *userdata, const char *key_str,
|
||||||
|
float **values, unsigned *out_num_values,
|
||||||
|
const float *default_values, unsigned num_default_values)
|
||||||
|
{
|
||||||
|
char key[2][256];
|
||||||
|
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
|
||||||
|
char *str = NULL;
|
||||||
|
|
||||||
|
fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0]));
|
||||||
|
fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1]));
|
||||||
|
|
||||||
|
if ( config_get_string(usr->conf, key[0], &str) ||
|
||||||
|
config_get_string(usr->conf, key[1], &str))
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct string_list *list = string_split(str, " ");
|
||||||
|
*values = (float*)calloc(list->size, sizeof(float));
|
||||||
|
for (i = 0; i < list->size; i++)
|
||||||
|
(*values)[i] = (float)strtod(list->elems[i].data, NULL);
|
||||||
|
*out_num_values = (unsigned)list->size;
|
||||||
|
string_list_free(list);
|
||||||
|
free(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
*values = (float*)calloc(num_default_values, sizeof(float));
|
||||||
|
memcpy(*values, default_values, sizeof(float) * num_default_values);
|
||||||
|
*out_num_values = num_default_values;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_userdata_get_int_array(void *userdata, const char *key_str,
|
||||||
|
int **values, unsigned *out_num_values,
|
||||||
|
const int *default_values, unsigned num_default_values)
|
||||||
|
{
|
||||||
|
char key[2][256];
|
||||||
|
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
|
||||||
|
char *str = NULL;
|
||||||
|
fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0]));
|
||||||
|
fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1]));
|
||||||
|
|
||||||
|
if ( config_get_string(usr->conf, key[0], &str) ||
|
||||||
|
config_get_string(usr->conf, key[1], &str))
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct string_list *list = string_split(str, " ");
|
||||||
|
*values = (int*)calloc(list->size, sizeof(int));
|
||||||
|
for (i = 0; i < list->size; i++)
|
||||||
|
(*values)[i] = (int)strtod(list->elems[i].data, NULL);
|
||||||
|
*out_num_values = (unsigned)list->size;
|
||||||
|
string_list_free(list);
|
||||||
|
free(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
*values = (int*)calloc(num_default_values, sizeof(int));
|
||||||
|
memcpy(*values, default_values, sizeof(int) * num_default_values);
|
||||||
|
*out_num_values = num_default_values;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_userdata_get_string(void *userdata, const char *key_str,
|
||||||
|
char **output, const char *default_output)
|
||||||
|
{
|
||||||
|
char key[2][256];
|
||||||
|
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
|
||||||
|
char *str = NULL;
|
||||||
|
fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0]));
|
||||||
|
fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1]));
|
||||||
|
|
||||||
|
if ( config_get_string(usr->conf, key[0], &str) ||
|
||||||
|
config_get_string(usr->conf, key[1], &str))
|
||||||
|
{
|
||||||
|
*output = str;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = strdup(default_output);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_userdata_free(void *ptr)
|
||||||
|
{
|
||||||
|
if (ptr)
|
||||||
|
free(ptr);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,85 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (nbio_intf.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <file/nbio.h>
|
||||||
|
|
||||||
|
extern nbio_intf_t nbio_linux;
|
||||||
|
extern nbio_intf_t nbio_mmap_unix;
|
||||||
|
extern nbio_intf_t nbio_mmap_win32;
|
||||||
|
extern nbio_intf_t nbio_stdio;
|
||||||
|
|
||||||
|
#if defined(_linux__)
|
||||||
|
static nbio_intf_t *internal_nbio = &nbio_linux;
|
||||||
|
#elif defined(HAVE_MMAP) && defined(BSD)
|
||||||
|
static nbio_intf_t *internal_nbio = &nbio_mmap_unix;
|
||||||
|
#elif defined(_WIN32) && !defined(_XBOX)
|
||||||
|
static nbio_intf_t *internal_nbio = &nbio_mmap_win32;
|
||||||
|
#else
|
||||||
|
static nbio_intf_t *internal_nbio = &nbio_stdio;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *nbio_open(const char * filename, unsigned mode)
|
||||||
|
{
|
||||||
|
return internal_nbio->open(filename, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nbio_begin_read(void *data)
|
||||||
|
{
|
||||||
|
internal_nbio->begin_read(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nbio_begin_write(void *data)
|
||||||
|
{
|
||||||
|
internal_nbio->begin_write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nbio_iterate(void *data)
|
||||||
|
{
|
||||||
|
return internal_nbio->iterate(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nbio_resize(void *data, size_t len)
|
||||||
|
{
|
||||||
|
internal_nbio->resize(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *nbio_get_ptr(void *data, size_t* len)
|
||||||
|
{
|
||||||
|
return internal_nbio->get_ptr(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nbio_cancel(void *data)
|
||||||
|
{
|
||||||
|
internal_nbio->cancel(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nbio_free(void *data)
|
||||||
|
{
|
||||||
|
internal_nbio->free(data);
|
||||||
|
}
|
|
@ -0,0 +1,245 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (nbio_linux.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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/nbio.h>
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <linux/aio_abi.h>
|
||||||
|
|
||||||
|
struct nbio_linux_t
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
bool busy;
|
||||||
|
|
||||||
|
aio_context_t ctx;
|
||||||
|
struct iocb cb;
|
||||||
|
|
||||||
|
void* ptr;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* there's also a Unix AIO thingy, but it's not in glibc
|
||||||
|
* and we don't want more dependencies */
|
||||||
|
|
||||||
|
static int io_setup(unsigned nr, aio_context_t * ctxp)
|
||||||
|
{
|
||||||
|
return syscall(__NR_io_setup, nr, ctxp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int io_destroy(aio_context_t ctx)
|
||||||
|
{
|
||||||
|
return syscall(__NR_io_destroy, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int io_submit(aio_context_t ctx, long nr, struct iocb ** cbp)
|
||||||
|
{
|
||||||
|
return syscall(__NR_io_submit, ctx, nr, cbp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int io_cancel(aio_context_t ctx, struct iocb * iocb, struct io_event * result)
|
||||||
|
{
|
||||||
|
return syscall(__NR_io_cancel, ctx, iocb, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int io_getevents(aio_context_t ctx, long min_nr, long nr,
|
||||||
|
struct io_event * events, struct timespec * timeout)
|
||||||
|
{
|
||||||
|
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_begin_op(struct nbio_linux_t* handle, uint16_t op)
|
||||||
|
{
|
||||||
|
struct iocb * cbp = &handle->cb;
|
||||||
|
|
||||||
|
memset(&handle->cb, 0, sizeof(handle->cb));
|
||||||
|
|
||||||
|
handle->cb.aio_fildes = handle->fd;
|
||||||
|
handle->cb.aio_lio_opcode = op;
|
||||||
|
|
||||||
|
handle->cb.aio_buf = (uint64_t)(uintptr_t)handle->ptr;
|
||||||
|
handle->cb.aio_offset = 0;
|
||||||
|
handle->cb.aio_nbytes = handle->len;
|
||||||
|
|
||||||
|
if (io_submit(handle->ctx, 1, &cbp) != 1)
|
||||||
|
{
|
||||||
|
puts("ERROR - io_submit() failed");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
handle->busy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *nbio_linux_open(const char * filename, unsigned mode)
|
||||||
|
{
|
||||||
|
static const int o_flags[] = { O_RDONLY, O_RDWR|O_CREAT|O_TRUNC, O_RDWR, O_RDONLY, O_RDWR|O_CREAT|O_TRUNC };
|
||||||
|
|
||||||
|
aio_context_t ctx = 0;
|
||||||
|
struct nbio_linux_t* handle = NULL;
|
||||||
|
int fd = open(filename, o_flags[mode]|O_CLOEXEC, 0644);
|
||||||
|
if (fd < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (io_setup(128, &ctx) < 0)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle = (struct nbio_linux_t*)malloc(sizeof(struct nbio_linux_t));
|
||||||
|
handle->fd = fd;
|
||||||
|
handle->ctx = ctx;
|
||||||
|
handle->len = lseek(fd, 0, SEEK_END);
|
||||||
|
handle->ptr = malloc(handle->len);
|
||||||
|
handle->busy = false;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_linux_begin_read(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
|
||||||
|
if (handle)
|
||||||
|
nbio_begin_op(handle, IOCB_CMD_PREAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_linux_begin_write(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
|
||||||
|
if (handle)
|
||||||
|
nbio_begin_op(handle, IOCB_CMD_PWRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nbio_linux_iterate(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return false;
|
||||||
|
if (handle->busy)
|
||||||
|
{
|
||||||
|
struct io_event ev;
|
||||||
|
if (io_getevents(handle->ctx, 0, 1, &ev, NULL) == 1)
|
||||||
|
handle->busy = false;
|
||||||
|
}
|
||||||
|
return !handle->busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_linux_resize(void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (len < handle->len)
|
||||||
|
{
|
||||||
|
/* this works perfectly fine if this check is removed, but it
|
||||||
|
* won't work on other nbio implementations */
|
||||||
|
/* therefore, it's blocked so nobody accidentally relies on it */
|
||||||
|
puts("ERROR - attempted file shrink operation, not implemented");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftruncate(handle->fd, len) != 0)
|
||||||
|
{
|
||||||
|
puts("ERROR - couldn't resize file (ftruncate)");
|
||||||
|
abort(); /* this one returns void and I can't find any other way
|
||||||
|
for it to report failure */
|
||||||
|
}
|
||||||
|
handle->ptr = realloc(handle->ptr, len);
|
||||||
|
handle->len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *nbio_linux_get_ptr(void *data, size_t* len)
|
||||||
|
{
|
||||||
|
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return NULL;
|
||||||
|
if (len)
|
||||||
|
*len = handle->len;
|
||||||
|
if (!handle->busy)
|
||||||
|
return handle->ptr;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_linux_cancel(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (handle->busy)
|
||||||
|
{
|
||||||
|
struct io_event ev;
|
||||||
|
io_cancel(handle->ctx, &handle->cb, &ev);
|
||||||
|
handle->busy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_linux_free(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
io_destroy(handle->ctx);
|
||||||
|
close(handle->fd);
|
||||||
|
free(handle->ptr);
|
||||||
|
free(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
nbio_intf_t nbio_linux = {
|
||||||
|
nbio_linux_open,
|
||||||
|
nbio_linux_begin_read,
|
||||||
|
nbio_linux_begin_write,
|
||||||
|
nbio_linux_iterate,
|
||||||
|
nbio_linux_resize,
|
||||||
|
nbio_linux_get_ptr,
|
||||||
|
nbio_linux_cancel,
|
||||||
|
nbio_linux_free,
|
||||||
|
"nbio_linux",
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
nbio_intf_t nbio_linux = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
"nbio_linux",
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,268 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (nbio_stdio.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <file/nbio.h>
|
||||||
|
#include <encodings/utf.h>
|
||||||
|
|
||||||
|
/* Assume W-functions do not work below Win2K and Xbox platforms */
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||||
|
|
||||||
|
#ifndef LEGACY_WIN32
|
||||||
|
#define LEGACY_WIN32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nbio_stdio_t
|
||||||
|
{
|
||||||
|
FILE* f;
|
||||||
|
void* data;
|
||||||
|
size_t progress;
|
||||||
|
size_t len;
|
||||||
|
/*
|
||||||
|
* possible values:
|
||||||
|
* NBIO_READ, NBIO_WRITE - obvious
|
||||||
|
* -1 - currently doing nothing
|
||||||
|
* -2 - the pointer was reallocated since the last operation
|
||||||
|
*/
|
||||||
|
signed char op;
|
||||||
|
signed char mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(_WIN32) || defined(LEGACY_WIN32)
|
||||||
|
static const char *stdio_modes[] = { "rb", "wb", "r+b", "rb", "wb", "r+b" };
|
||||||
|
#else
|
||||||
|
static const wchar_t *stdio_modes[] = { L"rb", L"wb", L"r+b", L"rb", L"wb", L"r+b" };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void *nbio_stdio_open(const char * filename, unsigned mode)
|
||||||
|
{
|
||||||
|
void *buf = NULL;
|
||||||
|
struct nbio_stdio_t* handle = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
#if !defined(_WIN32) || defined(LEGACY_WIN32)
|
||||||
|
FILE* f = fopen(filename, stdio_modes[mode]);
|
||||||
|
#else
|
||||||
|
wchar_t *filename_wide = utf8_to_utf16_string_alloc(filename);
|
||||||
|
FILE* f = _wfopen(filename_wide, stdio_modes[mode]);
|
||||||
|
|
||||||
|
if (filename_wide)
|
||||||
|
free(filename_wide);
|
||||||
|
#endif
|
||||||
|
if (!f)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
handle = (struct nbio_stdio_t*)malloc(sizeof(struct nbio_stdio_t));
|
||||||
|
|
||||||
|
if (!handle)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
handle->f = f;
|
||||||
|
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case NBIO_WRITE:
|
||||||
|
case BIO_WRITE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fseek(handle->f, 0, SEEK_END);
|
||||||
|
len = ftell(handle->f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle->mode = mode;
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
buf = malloc(len);
|
||||||
|
|
||||||
|
if (len && !buf)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
handle->data = buf;
|
||||||
|
handle->len = len;
|
||||||
|
handle->progress = handle->len;
|
||||||
|
handle->op = -2;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (handle)
|
||||||
|
free(handle);
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_stdio_begin_read(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (handle->op >= 0)
|
||||||
|
{
|
||||||
|
puts("ERROR - attempted file read operation while busy");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(handle->f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
handle->op = NBIO_READ;
|
||||||
|
handle->progress = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_stdio_begin_write(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (handle->op >= 0)
|
||||||
|
{
|
||||||
|
puts("ERROR - attempted file write operation while busy");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(handle->f, 0, SEEK_SET);
|
||||||
|
handle->op = NBIO_WRITE;
|
||||||
|
handle->progress = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nbio_stdio_iterate(void *data)
|
||||||
|
{
|
||||||
|
size_t amount = 65536;
|
||||||
|
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
|
||||||
|
|
||||||
|
if (!handle)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (amount > handle->len - handle->progress)
|
||||||
|
amount = handle->len - handle->progress;
|
||||||
|
|
||||||
|
switch (handle->op)
|
||||||
|
{
|
||||||
|
case NBIO_READ:
|
||||||
|
if (handle->mode == BIO_READ)
|
||||||
|
{
|
||||||
|
amount = handle->len;
|
||||||
|
fread((char*)handle->data, 1, amount, handle->f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fread((char*)handle->data + handle->progress, 1, amount, handle->f);
|
||||||
|
break;
|
||||||
|
case NBIO_WRITE:
|
||||||
|
if (handle->mode == BIO_WRITE)
|
||||||
|
{
|
||||||
|
size_t written = 0;
|
||||||
|
amount = handle->len;
|
||||||
|
written = fwrite((char*)handle->data, 1, amount, handle->f);
|
||||||
|
if (written != amount)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fwrite((char*)handle->data + handle->progress, 1, amount, handle->f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle->progress += amount;
|
||||||
|
|
||||||
|
if (handle->progress == handle->len)
|
||||||
|
handle->op = -1;
|
||||||
|
return (handle->op < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_stdio_resize(void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (handle->op >= 0)
|
||||||
|
{
|
||||||
|
puts("ERROR - attempted file resize operation while busy");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (len < handle->len)
|
||||||
|
{
|
||||||
|
puts("ERROR - attempted file shrink operation, not implemented");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
handle->len = len;
|
||||||
|
handle->data = realloc(handle->data, handle->len);
|
||||||
|
handle->op = -1;
|
||||||
|
handle->progress = handle->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *nbio_stdio_get_ptr(void *data, size_t* len)
|
||||||
|
{
|
||||||
|
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return NULL;
|
||||||
|
if (len)
|
||||||
|
*len = handle->len;
|
||||||
|
if (handle->op == -1)
|
||||||
|
return handle->data;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_stdio_cancel(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
handle->op = -1;
|
||||||
|
handle->progress = handle->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_stdio_free(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
if (handle->op >= 0)
|
||||||
|
{
|
||||||
|
puts("ERROR - attempted free() while busy");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
fclose(handle->f);
|
||||||
|
free(handle->data);
|
||||||
|
|
||||||
|
handle->f = NULL;
|
||||||
|
handle->data = NULL;
|
||||||
|
free(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
nbio_intf_t nbio_stdio = {
|
||||||
|
nbio_stdio_open,
|
||||||
|
nbio_stdio_begin_read,
|
||||||
|
nbio_stdio_begin_write,
|
||||||
|
nbio_stdio_iterate,
|
||||||
|
nbio_stdio_resize,
|
||||||
|
nbio_stdio_get_ptr,
|
||||||
|
nbio_stdio_cancel,
|
||||||
|
nbio_stdio_free,
|
||||||
|
"nbio_stdio",
|
||||||
|
};
|
|
@ -0,0 +1,191 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (nbio_unixmmap.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <file/nbio.h>
|
||||||
|
|
||||||
|
#if defined(HAVE_MMAP) && defined(BSD)
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <direct.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
#ifndef O_CLOEXEC
|
||||||
|
#define O_CLOEXEC 0x1000000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifndef O_CLOEXEC
|
||||||
|
#define O_CLOEXEC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nbio_mmap_unix_t
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int map_flags;
|
||||||
|
size_t len;
|
||||||
|
void* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *nbio_mmap_unix_open(const char * filename, unsigned mode)
|
||||||
|
{
|
||||||
|
static const int o_flags[] = { O_RDONLY, O_RDWR|O_CREAT|O_TRUNC, O_RDWR, O_RDONLY, O_RDWR|O_CREAT|O_TRUNC };
|
||||||
|
static const int map_flags[] = { PROT_READ, PROT_WRITE|PROT_READ, PROT_WRITE|PROT_READ, PROT_READ, PROT_WRITE|PROT_READ };
|
||||||
|
|
||||||
|
size_t len;
|
||||||
|
void* ptr = NULL;
|
||||||
|
struct nbio_mmap_unix_t* handle = NULL;
|
||||||
|
int fd = open(filename, o_flags[mode]|O_CLOEXEC, 0644);
|
||||||
|
if (fd < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
len = lseek(fd, 0, SEEK_END);
|
||||||
|
if (len != 0)
|
||||||
|
ptr = mmap(NULL, len, map_flags[mode], MAP_SHARED, fd, 0);
|
||||||
|
|
||||||
|
if (ptr == MAP_FAILED)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle = malloc(sizeof(struct nbio_mmap_unix_t));
|
||||||
|
handle->fd = fd;
|
||||||
|
handle->map_flags = map_flags[mode];
|
||||||
|
handle->len = len;
|
||||||
|
handle->ptr = ptr;
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_unix_begin_read(void *data)
|
||||||
|
{
|
||||||
|
/* not needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_unix_begin_write(void *data)
|
||||||
|
{
|
||||||
|
/* not needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nbio_mmap_unix_iterate(void *data)
|
||||||
|
{
|
||||||
|
return true; /* not needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_unix_resize(void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
if (len < handle->len)
|
||||||
|
{
|
||||||
|
/* this works perfectly fine if this check is removed, but it
|
||||||
|
* won't work on other nbio implementations */
|
||||||
|
/* therefore, it's blocked so nobody accidentally relies on it */
|
||||||
|
puts("ERROR - attempted file shrink operation, not implemented");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftruncate(handle->fd, len) != 0)
|
||||||
|
{
|
||||||
|
puts("ERROR - couldn't resize file (ftruncate)");
|
||||||
|
abort(); /* this one returns void and I can't find any other
|
||||||
|
way for it to report failure */
|
||||||
|
}
|
||||||
|
|
||||||
|
munmap(handle->ptr, handle->len);
|
||||||
|
|
||||||
|
handle->ptr = mmap(NULL, len, handle->map_flags, MAP_SHARED, handle->fd, 0);
|
||||||
|
handle->len = len;
|
||||||
|
|
||||||
|
if (handle->ptr == MAP_FAILED)
|
||||||
|
{
|
||||||
|
puts("ERROR - couldn't resize file (mmap)");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *nbio_mmap_unix_get_ptr(void *data, size_t* len)
|
||||||
|
{
|
||||||
|
struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return NULL;
|
||||||
|
if (len)
|
||||||
|
*len = handle->len;
|
||||||
|
return handle->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_unix_cancel(void *data)
|
||||||
|
{
|
||||||
|
/* not needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_unix_free(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
close(handle->fd);
|
||||||
|
munmap(handle->ptr, handle->len);
|
||||||
|
free(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
nbio_intf_t nbio_mmap_unix = {
|
||||||
|
nbio_mmap_unix_open,
|
||||||
|
nbio_mmap_unix_begin_read,
|
||||||
|
nbio_mmap_unix_begin_write,
|
||||||
|
nbio_mmap_unix_iterate,
|
||||||
|
nbio_mmap_unix_resize,
|
||||||
|
nbio_mmap_unix_get_ptr,
|
||||||
|
nbio_mmap_unix_cancel,
|
||||||
|
nbio_mmap_unix_free,
|
||||||
|
"nbio_mmap_unix",
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
nbio_intf_t nbio_mmap_unix = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
"nbio_mmap_unix",
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,224 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (nbio_windowsmmap.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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/nbio.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(_XBOX)
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <encodings/utf.h>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
/* Assume W-functions do not work below Win2K and Xbox platforms */
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||||
|
|
||||||
|
#ifndef LEGACY_WIN32
|
||||||
|
#define LEGACY_WIN32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FILE_SHARE_ALL
|
||||||
|
#define FILE_SHARE_ALL (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nbio_mmap_win32_t
|
||||||
|
{
|
||||||
|
HANDLE file;
|
||||||
|
bool is_write;
|
||||||
|
size_t len;
|
||||||
|
void* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *nbio_mmap_win32_open(const char * filename, unsigned mode)
|
||||||
|
{
|
||||||
|
static const DWORD dispositions[] = { OPEN_EXISTING, CREATE_ALWAYS, OPEN_ALWAYS, OPEN_EXISTING, CREATE_ALWAYS };
|
||||||
|
HANDLE mem;
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
|
||||||
|
LARGE_INTEGER len;
|
||||||
|
#else
|
||||||
|
SIZE_T len;
|
||||||
|
#endif
|
||||||
|
struct nbio_mmap_win32_t* handle = NULL;
|
||||||
|
void* ptr = NULL;
|
||||||
|
bool is_write = (mode == NBIO_WRITE || mode == NBIO_UPDATE || mode == BIO_WRITE);
|
||||||
|
DWORD access = (is_write ? GENERIC_READ|GENERIC_WRITE : GENERIC_READ);
|
||||||
|
#if !defined(_WIN32) || defined(LEGACY_WIN32)
|
||||||
|
HANDLE file = CreateFile(filename, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
#else
|
||||||
|
wchar_t *filename_wide = utf8_to_utf16_string_alloc(filename);
|
||||||
|
HANDLE file = CreateFileW(filename_wide, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
|
||||||
|
if (filename_wide)
|
||||||
|
free(filename_wide);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (file == INVALID_HANDLE_VALUE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
|
||||||
|
/* GetFileSizeEx is new for Windows 2000 */
|
||||||
|
GetFileSizeEx(file, &len);
|
||||||
|
mem = CreateFileMapping(file, NULL, is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
|
||||||
|
ptr = MapViewOfFile(mem, is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len.QuadPart);
|
||||||
|
#else
|
||||||
|
GetFileSize(file, &len);
|
||||||
|
mem = CreateFileMapping(file, NULL, is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
|
||||||
|
ptr = MapViewOfFile(mem, is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CloseHandle(mem);
|
||||||
|
|
||||||
|
handle = (struct nbio_mmap_win32_t*)malloc(sizeof(struct nbio_mmap_win32_t));
|
||||||
|
|
||||||
|
handle->file = file;
|
||||||
|
handle->is_write = is_write;
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
|
||||||
|
handle->len = len.QuadPart;
|
||||||
|
#else
|
||||||
|
handle->len = len;
|
||||||
|
#endif
|
||||||
|
handle->ptr = ptr;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_win32_begin_read(void *data)
|
||||||
|
{
|
||||||
|
/* not needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_win32_begin_write(void *data)
|
||||||
|
{
|
||||||
|
/* not needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nbio_mmap_win32_iterate(void *data)
|
||||||
|
{
|
||||||
|
/* not needed */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_win32_resize(void *data, size_t len)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
|
||||||
|
LARGE_INTEGER len_li;
|
||||||
|
#else
|
||||||
|
SIZE_T len_li;
|
||||||
|
#endif
|
||||||
|
HANDLE mem;
|
||||||
|
struct nbio_mmap_win32_t* handle = (struct nbio_mmap_win32_t*)data;
|
||||||
|
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (len < handle->len)
|
||||||
|
{
|
||||||
|
/* this works perfectly fine if this check is removed,
|
||||||
|
* but it won't work on other nbio implementations */
|
||||||
|
/* therefore, it's blocked so nobody accidentally
|
||||||
|
* relies on it. */
|
||||||
|
puts("ERROR - attempted file shrink operation, not implemented");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
|
||||||
|
/* SetFilePointerEx is new for Windows 2000 */
|
||||||
|
len_li.QuadPart = len;
|
||||||
|
SetFilePointerEx(handle->file, len_li, NULL, FILE_BEGIN);
|
||||||
|
#else
|
||||||
|
len_li = len;
|
||||||
|
SetFilePointer(handle->file, len_li, NULL, FILE_BEGIN);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!SetEndOfFile(handle->file))
|
||||||
|
{
|
||||||
|
puts("ERROR - couldn't resize file (SetEndOfFile)");
|
||||||
|
abort(); /* this one returns void and I can't find any other way for it to report failure */
|
||||||
|
}
|
||||||
|
handle->len = len;
|
||||||
|
|
||||||
|
UnmapViewOfFile(handle->ptr);
|
||||||
|
mem = CreateFileMapping(handle->file, NULL, handle->is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
|
||||||
|
handle->ptr = MapViewOfFile(mem, handle->is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len);
|
||||||
|
CloseHandle(mem);
|
||||||
|
|
||||||
|
if (!handle->ptr)
|
||||||
|
{
|
||||||
|
puts("ERROR - couldn't resize file (MapViewOfFile)");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *nbio_mmap_win32_get_ptr(void *data, size_t* len)
|
||||||
|
{
|
||||||
|
struct nbio_mmap_win32_t* handle = (struct nbio_mmap_win32_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return NULL;
|
||||||
|
if (len)
|
||||||
|
*len = handle->len;
|
||||||
|
return handle->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_win32_cancel(void *data)
|
||||||
|
{
|
||||||
|
/* not needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbio_mmap_win32_free(void *data)
|
||||||
|
{
|
||||||
|
struct nbio_mmap_win32_t* handle = (struct nbio_mmap_win32_t*)data;
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
CloseHandle(handle->file);
|
||||||
|
UnmapViewOfFile(handle->ptr);
|
||||||
|
free(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
nbio_intf_t nbio_mmap_win32 = {
|
||||||
|
nbio_mmap_win32_open,
|
||||||
|
nbio_mmap_win32_begin_read,
|
||||||
|
nbio_mmap_win32_begin_write,
|
||||||
|
nbio_mmap_win32_iterate,
|
||||||
|
nbio_mmap_win32_resize,
|
||||||
|
nbio_mmap_win32_get_ptr,
|
||||||
|
nbio_mmap_win32_cancel,
|
||||||
|
nbio_mmap_win32_free,
|
||||||
|
"nbio_mmap_win32",
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
nbio_intf_t nbio_mmap_win32 = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
"nbio_mmap_win32",
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,294 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (retro_dirent.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <retro_common.h>
|
||||||
|
|
||||||
|
#include <boolean.h>
|
||||||
|
#include <retro_dirent.h>
|
||||||
|
#include <encodings/utf.h>
|
||||||
|
#include <compat/strl.h>
|
||||||
|
#include <string/stdstring.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
# define setmode _setmode
|
||||||
|
# endif
|
||||||
|
#include <sys/stat.h>
|
||||||
|
# ifdef _XBOX
|
||||||
|
# include <xtl.h>
|
||||||
|
# define INVALID_FILE_ATTRIBUTES -1
|
||||||
|
# else
|
||||||
|
# include <io.h>
|
||||||
|
# include <fcntl.h>
|
||||||
|
# include <direct.h>
|
||||||
|
# include <windows.h>
|
||||||
|
# endif
|
||||||
|
#elif defined(VITA)
|
||||||
|
# include <psp2/io/fcntl.h>
|
||||||
|
# include <psp2/io/dirent.h>
|
||||||
|
#include <psp2/io/stat.h>
|
||||||
|
#else
|
||||||
|
# if defined(PSP)
|
||||||
|
# include <pspiofilemgr.h>
|
||||||
|
# endif
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <sys/stat.h>
|
||||||
|
# include <dirent.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __CELLOS_LV2__
|
||||||
|
#include <cell/cell_fs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
|
||||||
|
#include <unistd.h> /* stat() is defined here */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||||
|
#ifndef LEGACY_WIN32
|
||||||
|
#define LEGACY_WIN32
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct RDIR
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if defined(LEGACY_WIN32)
|
||||||
|
WIN32_FIND_DATA entry;
|
||||||
|
#else
|
||||||
|
WIN32_FIND_DATAW entry;
|
||||||
|
#endif
|
||||||
|
HANDLE directory;
|
||||||
|
bool next;
|
||||||
|
char path[PATH_MAX_LENGTH];
|
||||||
|
#elif defined(VITA) || defined(PSP)
|
||||||
|
SceUID directory;
|
||||||
|
SceIoDirent entry;
|
||||||
|
#elif defined(__CELLOS_LV2__)
|
||||||
|
CellFsErrno error;
|
||||||
|
int directory;
|
||||||
|
CellFsDirent entry;
|
||||||
|
#else
|
||||||
|
DIR *directory;
|
||||||
|
const struct dirent *entry;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RDIR *retro_opendir(const char *name)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
char path_buf[1024];
|
||||||
|
char *path_local = NULL;
|
||||||
|
wchar_t *path_wide = NULL;
|
||||||
|
unsigned path_len;
|
||||||
|
#endif
|
||||||
|
struct RDIR *rdir = (struct RDIR*)calloc(1, sizeof(*rdir));
|
||||||
|
|
||||||
|
if (!rdir)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
(void)path_wide;
|
||||||
|
(void)path_local;
|
||||||
|
|
||||||
|
path_buf[0] = '\0';
|
||||||
|
path_len = strlen(name);
|
||||||
|
|
||||||
|
/* Non-NT platforms don't like extra slashes in the path */
|
||||||
|
if (name[path_len - 1] == '\\')
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%s*", name);
|
||||||
|
else
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%s\\*", name);
|
||||||
|
|
||||||
|
#if defined(LEGACY_WIN32)
|
||||||
|
path_local = utf8_to_local_string_alloc(path_buf);
|
||||||
|
rdir->directory = FindFirstFile(path_local, &rdir->entry);
|
||||||
|
|
||||||
|
if (path_local)
|
||||||
|
free(path_local);
|
||||||
|
#else
|
||||||
|
path_wide = utf8_to_utf16_string_alloc(path_buf);
|
||||||
|
rdir->directory = FindFirstFileW(path_wide, &rdir->entry);
|
||||||
|
|
||||||
|
if (path_wide)
|
||||||
|
free(path_wide);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(VITA) || defined(PSP)
|
||||||
|
rdir->directory = sceIoDopen(name);
|
||||||
|
#elif defined(_3DS)
|
||||||
|
rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
|
||||||
|
rdir->entry = NULL;
|
||||||
|
#elif defined(__CELLOS_LV2__)
|
||||||
|
rdir->error = cellFsOpendir(name, &rdir->directory);
|
||||||
|
#else
|
||||||
|
rdir->directory = opendir(name);
|
||||||
|
rdir->entry = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (rdir->directory)
|
||||||
|
return rdir;
|
||||||
|
|
||||||
|
free(rdir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool retro_dirent_error(struct RDIR *rdir)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
return (rdir->directory == INVALID_HANDLE_VALUE);
|
||||||
|
#elif defined(VITA) || defined(PSP)
|
||||||
|
return (rdir->directory < 0);
|
||||||
|
#elif defined(__CELLOS_LV2__)
|
||||||
|
return (rdir->error != CELL_FS_SUCCEEDED);
|
||||||
|
#else
|
||||||
|
return !(rdir->directory);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int retro_readdir(struct RDIR *rdir)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if(rdir->next)
|
||||||
|
#if defined(LEGACY_WIN32)
|
||||||
|
return (FindNextFile(rdir->directory, &rdir->entry) != 0);
|
||||||
|
#else
|
||||||
|
return (FindNextFileW(rdir->directory, &rdir->entry) != 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rdir->next = true;
|
||||||
|
return (rdir->directory != INVALID_HANDLE_VALUE);
|
||||||
|
#elif defined(VITA) || defined(PSP)
|
||||||
|
return (sceIoDread(rdir->directory, &rdir->entry) > 0);
|
||||||
|
#elif defined(__CELLOS_LV2__)
|
||||||
|
uint64_t nread;
|
||||||
|
rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread);
|
||||||
|
return (nread != 0);
|
||||||
|
#else
|
||||||
|
return ((rdir->entry = readdir(rdir->directory)) != NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *retro_dirent_get_name(struct RDIR *rdir)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if defined(LEGACY_WIN32)
|
||||||
|
char *name_local = local_to_utf8_string_alloc(rdir->entry.cFileName);
|
||||||
|
memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
|
||||||
|
strlcpy(rdir->entry.cFileName, name_local, sizeof(rdir->entry.cFileName));
|
||||||
|
|
||||||
|
if (name_local)
|
||||||
|
free(name_local);
|
||||||
|
#else
|
||||||
|
char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName);
|
||||||
|
memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
|
||||||
|
strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName));
|
||||||
|
|
||||||
|
if (name)
|
||||||
|
free(name);
|
||||||
|
#endif
|
||||||
|
return (char*)rdir->entry.cFileName;
|
||||||
|
#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__)
|
||||||
|
return rdir->entry.d_name;
|
||||||
|
#else
|
||||||
|
|
||||||
|
return rdir->entry->d_name;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* retro_dirent_is_dir:
|
||||||
|
* @rdir : pointer to the directory entry.
|
||||||
|
* @path : path to the directory entry.
|
||||||
|
*
|
||||||
|
* Is the directory listing entry a directory?
|
||||||
|
*
|
||||||
|
* Returns: true if directory listing entry is
|
||||||
|
* a directory, false if not.
|
||||||
|
*/
|
||||||
|
bool retro_dirent_is_dir(struct RDIR *rdir, const char *path)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
|
||||||
|
return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||||
|
#elif defined(PSP) || defined(VITA)
|
||||||
|
const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
|
||||||
|
#if defined(PSP)
|
||||||
|
return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
|
||||||
|
#elif defined(VITA)
|
||||||
|
return SCE_S_ISDIR(entry->d_stat.st_mode);
|
||||||
|
#endif
|
||||||
|
#elif defined(__CELLOS_LV2__)
|
||||||
|
CellFsDirent *entry = (CellFsDirent*)&rdir->entry;
|
||||||
|
return (entry->d_type == CELL_FS_TYPE_DIRECTORY);
|
||||||
|
#else
|
||||||
|
struct stat buf;
|
||||||
|
#if defined(DT_DIR)
|
||||||
|
const struct dirent *entry = (const struct dirent*)rdir->entry;
|
||||||
|
if (entry->d_type == DT_DIR)
|
||||||
|
return true;
|
||||||
|
/* This can happen on certain file systems. */
|
||||||
|
if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
/* dirent struct doesn't have d_type, do it the slow way ... */
|
||||||
|
if (stat(path, &buf) < 0)
|
||||||
|
return false;
|
||||||
|
return S_ISDIR(buf.st_mode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void retro_dirent_include_hidden(struct RDIR *rdir, bool include_hidden)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (include_hidden)
|
||||||
|
rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
|
||||||
|
else
|
||||||
|
rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void retro_closedir(struct RDIR *rdir)
|
||||||
|
{
|
||||||
|
if (!rdir)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if (rdir->directory != INVALID_HANDLE_VALUE)
|
||||||
|
FindClose(rdir->directory);
|
||||||
|
#elif defined(VITA) || defined(PSP)
|
||||||
|
sceIoDclose(rdir->directory);
|
||||||
|
#elif defined(__CELLOS_LV2__)
|
||||||
|
rdir->error = cellFsClosedir(rdir->directory);
|
||||||
|
#else
|
||||||
|
if (rdir->directory)
|
||||||
|
closedir(rdir->directory);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
free(rdir);
|
||||||
|
}
|
|
@ -0,0 +1,623 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rbmp.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Modified version of stb_image's BMP sources. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h> /* ptrdiff_t on osx */
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_inline.h>
|
||||||
|
|
||||||
|
#include <formats/image.h>
|
||||||
|
#include <formats/rbmp.h>
|
||||||
|
|
||||||
|
/* truncate int to byte without warnings */
|
||||||
|
#define RBMP__BYTECAST(x) ((unsigned char) ((x) & 255))
|
||||||
|
|
||||||
|
#define RBMP_COMPUTE_Y(r, g, b) ((unsigned char) ((((r) * 77) + ((g) * 150) + (29 * (b))) >> 8))
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t img_x;
|
||||||
|
uint32_t img_y;
|
||||||
|
int img_n;
|
||||||
|
int img_out_n;
|
||||||
|
|
||||||
|
int buflen;
|
||||||
|
unsigned char buffer_start[128];
|
||||||
|
|
||||||
|
unsigned char *img_buffer;
|
||||||
|
unsigned char *img_buffer_end;
|
||||||
|
unsigned char *img_buffer_original;
|
||||||
|
} rbmp__context;
|
||||||
|
|
||||||
|
struct rbmp
|
||||||
|
{
|
||||||
|
uint8_t *buff_data;
|
||||||
|
uint32_t *output_image;
|
||||||
|
};
|
||||||
|
|
||||||
|
static INLINE unsigned char rbmp__get8(rbmp__context *s)
|
||||||
|
{
|
||||||
|
if (s->img_buffer < s->img_buffer_end)
|
||||||
|
return *s->img_buffer++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rbmp__skip(rbmp__context *s, int n)
|
||||||
|
{
|
||||||
|
if (n < 0)
|
||||||
|
{
|
||||||
|
s->img_buffer = s->img_buffer_end;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->img_buffer += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rbmp__get16le(rbmp__context *s)
|
||||||
|
{
|
||||||
|
int z = rbmp__get8(s);
|
||||||
|
return z + (rbmp__get8(s) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t rbmp__get32le(rbmp__context *s)
|
||||||
|
{
|
||||||
|
uint32_t z = rbmp__get16le(s);
|
||||||
|
return z + (rbmp__get16le(s) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *rbmp__convert_format(
|
||||||
|
unsigned char *data,
|
||||||
|
int img_n,
|
||||||
|
int req_comp,
|
||||||
|
unsigned int x,
|
||||||
|
unsigned int y)
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
unsigned char *good = (unsigned char *)malloc(req_comp * x * y);
|
||||||
|
|
||||||
|
if (!good)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (j=0; j < (int) y; ++j)
|
||||||
|
{
|
||||||
|
unsigned char *src = data + j * x * img_n ;
|
||||||
|
unsigned char *dest = good + j * x * req_comp;
|
||||||
|
|
||||||
|
switch (((img_n)*8+(req_comp)))
|
||||||
|
{
|
||||||
|
case ((1)*8+(2)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 1, dest += 2)
|
||||||
|
dest[0]=src[0], dest[1]=255;
|
||||||
|
break;
|
||||||
|
case ((1)*8+(3)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 1, dest += 3)
|
||||||
|
dest[0]=dest[1]=dest[2]=src[0];
|
||||||
|
break;
|
||||||
|
case ((1)*8+(4)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 1, dest += 4)
|
||||||
|
dest[0]=dest[1]=dest[2]=src[0], dest[3]=255;
|
||||||
|
break;
|
||||||
|
case ((2)*8+(1)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 2, dest += 1)
|
||||||
|
dest[0]=src[0];
|
||||||
|
break;
|
||||||
|
case ((2)*8+(3)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 2, dest += 3)
|
||||||
|
dest[0]=dest[1]=dest[2]=src[0];
|
||||||
|
break;
|
||||||
|
case ((2)*8+(4)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 2, dest += 4)
|
||||||
|
dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];
|
||||||
|
break;
|
||||||
|
case ((3)*8+(4)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 3, dest += 4)
|
||||||
|
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255;
|
||||||
|
break;
|
||||||
|
case ((3)*8+(1)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 3, dest += 1)
|
||||||
|
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
|
||||||
|
break;
|
||||||
|
case ((3)*8+(2)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 3, dest += 2)
|
||||||
|
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = 255;
|
||||||
|
break;
|
||||||
|
case ((4)*8+(1)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 4, dest += 1)
|
||||||
|
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
|
||||||
|
break;
|
||||||
|
case ((4)*8+(2)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 4, dest += 2)
|
||||||
|
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = src[3];
|
||||||
|
break;
|
||||||
|
case ((4)*8+(3)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 4, dest += 3)
|
||||||
|
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return good;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Microsoft/Windows BMP image */
|
||||||
|
|
||||||
|
/* returns 0..31 for the highest set bit */
|
||||||
|
static int rbmp__high_bit(unsigned int z)
|
||||||
|
{
|
||||||
|
int n=0;
|
||||||
|
if (z == 0)
|
||||||
|
return -1;
|
||||||
|
if (z >= 0x10000) n += 16, z >>= 16;
|
||||||
|
if (z >= 0x00100) n += 8, z >>= 8;
|
||||||
|
if (z >= 0x00010) n += 4, z >>= 4;
|
||||||
|
if (z >= 0x00004) n += 2, z >>= 2;
|
||||||
|
if (z >= 0x00002) n += 1, z >>= 1;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rbmp__bitcount(unsigned int a)
|
||||||
|
{
|
||||||
|
a = (a & 0x55555555) + ((a >> 1) & 0x55555555); /* max 2 */
|
||||||
|
a = (a & 0x33333333) + ((a >> 2) & 0x33333333); /* max 4 */
|
||||||
|
a = (a + (a >> 4)) & 0x0f0f0f0f; /* max 8 per 4, now 8 bits */
|
||||||
|
a = (a + (a >> 8)); /* max 16 per 8 bits */
|
||||||
|
a = (a + (a >> 16)); /* max 32 per 8 bits */
|
||||||
|
return a & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rbmp__shiftsigned(int v, int shift, int bits)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int z=0;
|
||||||
|
|
||||||
|
if (shift < 0)
|
||||||
|
v <<= -shift;
|
||||||
|
else
|
||||||
|
v >>= shift;
|
||||||
|
|
||||||
|
result = v;
|
||||||
|
z = bits;
|
||||||
|
|
||||||
|
while (z < 8)
|
||||||
|
{
|
||||||
|
result += v >> z;
|
||||||
|
z += bits;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *rbmp__bmp_load(rbmp__context *s, unsigned *x, unsigned *y,
|
||||||
|
int *comp, int req_comp)
|
||||||
|
{
|
||||||
|
unsigned char *out;
|
||||||
|
int bpp, flip_vertically, pad, target, offset, hsz;
|
||||||
|
int psize=0,i,j,width;
|
||||||
|
unsigned int mr=0,mg=0,mb=0,ma=0;
|
||||||
|
|
||||||
|
/* Corrupt BMP? */
|
||||||
|
if (rbmp__get8(s) != 'B' || rbmp__get8(s) != 'M')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rbmp__get32le(s); /* discard filesize */
|
||||||
|
rbmp__get16le(s); /* discard reserved */
|
||||||
|
rbmp__get16le(s); /* discard reserved */
|
||||||
|
offset = rbmp__get32le(s);
|
||||||
|
hsz = rbmp__get32le(s);
|
||||||
|
|
||||||
|
/* BMP type not supported? */
|
||||||
|
if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (hsz == 12)
|
||||||
|
{
|
||||||
|
s->img_x = rbmp__get16le(s);
|
||||||
|
s->img_y = rbmp__get16le(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s->img_x = rbmp__get32le(s);
|
||||||
|
s->img_y = rbmp__get32le(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bad BMP? */
|
||||||
|
if (rbmp__get16le(s) != 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bpp = rbmp__get16le(s);
|
||||||
|
|
||||||
|
/* BMP 1-bit type not supported? */
|
||||||
|
if (bpp == 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
flip_vertically = ((int) s->img_y) > 0;
|
||||||
|
s->img_y = abs((int) s->img_y);
|
||||||
|
|
||||||
|
if (hsz == 12)
|
||||||
|
{
|
||||||
|
if (bpp < 24)
|
||||||
|
psize = (offset - 14 - 24) / 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int compress = rbmp__get32le(s);
|
||||||
|
|
||||||
|
/* BMP RLE type not supported? */
|
||||||
|
if (compress == 1 || compress == 2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rbmp__get32le(s); /* discard sizeof */
|
||||||
|
rbmp__get32le(s); /* discard hres */
|
||||||
|
rbmp__get32le(s); /* discard vres */
|
||||||
|
rbmp__get32le(s); /* discard colors used */
|
||||||
|
rbmp__get32le(s); /* discard max important */
|
||||||
|
|
||||||
|
if (hsz == 40 || hsz == 56)
|
||||||
|
{
|
||||||
|
if (hsz == 56)
|
||||||
|
{
|
||||||
|
rbmp__get32le(s);
|
||||||
|
rbmp__get32le(s);
|
||||||
|
rbmp__get32le(s);
|
||||||
|
rbmp__get32le(s);
|
||||||
|
}
|
||||||
|
if (bpp == 16 || bpp == 32)
|
||||||
|
{
|
||||||
|
switch (compress)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
#if 0
|
||||||
|
if (bpp == 32)
|
||||||
|
{
|
||||||
|
mr = 0xffu << 16;
|
||||||
|
mg = 0xffu << 8;
|
||||||
|
mb = 0xffu << 0;
|
||||||
|
ma = 0xffu << 24;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mr = 31u << 10;
|
||||||
|
mg = 31u << 5;
|
||||||
|
mb = 31u << 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
mr = rbmp__get32le(s);
|
||||||
|
mg = rbmp__get32le(s);
|
||||||
|
mb = rbmp__get32le(s);
|
||||||
|
/* not documented, but generated by
|
||||||
|
* Photoshop and handled by MS Paint */
|
||||||
|
/* Bad BMP ?*/
|
||||||
|
if (mr == mg && mg == mb)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
#if 0
|
||||||
|
mr = mg = mb = 0;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bad BMP? */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mr = rbmp__get32le(s);
|
||||||
|
mg = rbmp__get32le(s);
|
||||||
|
mb = rbmp__get32le(s);
|
||||||
|
ma = rbmp__get32le(s);
|
||||||
|
rbmp__get32le(s); /* Discard color space */
|
||||||
|
for (i=0; i < 12; ++i)
|
||||||
|
rbmp__get32le(s); /* Discard color space parameters */
|
||||||
|
if (hsz == 124)
|
||||||
|
{
|
||||||
|
rbmp__get32le(s); /* Discard rendering intent */
|
||||||
|
rbmp__get32le(s); /* Discard offset of profile data */
|
||||||
|
rbmp__get32le(s); /* Discard size of profile data */
|
||||||
|
rbmp__get32le(s); /* Discard reserved */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bpp < 16)
|
||||||
|
psize = (offset - 14 - hsz) >> 2;
|
||||||
|
}
|
||||||
|
s->img_n = ma ? 4 : 3;
|
||||||
|
if (req_comp && req_comp >= 3) /* We can directly decode 3 or 4 */
|
||||||
|
target = req_comp;
|
||||||
|
else
|
||||||
|
target = s->img_n; /* If they want monochrome, we'll post-convert */
|
||||||
|
|
||||||
|
out = (unsigned char *) malloc(target * s->img_x * s->img_y);
|
||||||
|
|
||||||
|
if (!out)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (bpp < 16)
|
||||||
|
{
|
||||||
|
unsigned char pal[256][4];
|
||||||
|
int z=0;
|
||||||
|
|
||||||
|
/* Corrupt BMP? */
|
||||||
|
if (psize == 0 || psize > 256)
|
||||||
|
{
|
||||||
|
free(out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i < psize; ++i)
|
||||||
|
{
|
||||||
|
pal[i][2] = rbmp__get8(s);
|
||||||
|
pal[i][1] = rbmp__get8(s);
|
||||||
|
pal[i][0] = rbmp__get8(s);
|
||||||
|
if (hsz != 12) rbmp__get8(s);
|
||||||
|
pal[i][3] = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
rbmp__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
|
||||||
|
if (bpp == 4)
|
||||||
|
width = (s->img_x + 1) >> 1;
|
||||||
|
else if (bpp == 8)
|
||||||
|
width = s->img_x;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Corrupt BMP */
|
||||||
|
free(out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pad = (-width)&3;
|
||||||
|
for (j=0; j < (int) s->img_y; ++j)
|
||||||
|
{
|
||||||
|
for (i=0; i < (int) s->img_x; i += 2)
|
||||||
|
{
|
||||||
|
int v=rbmp__get8(s),v2=0;
|
||||||
|
if (bpp == 4)
|
||||||
|
{
|
||||||
|
v2 = v & 15;
|
||||||
|
v >>= 4;
|
||||||
|
}
|
||||||
|
out[z++] = pal[v][0];
|
||||||
|
out[z++] = pal[v][1];
|
||||||
|
out[z++] = pal[v][2];
|
||||||
|
if (target == 4)
|
||||||
|
out[z++] = 255;
|
||||||
|
|
||||||
|
if (i+1 == (int)s->img_x)
|
||||||
|
break;
|
||||||
|
|
||||||
|
v = (bpp == 8) ? rbmp__get8(s) : v2;
|
||||||
|
out[z++] = pal[v][0];
|
||||||
|
out[z++] = pal[v][1];
|
||||||
|
out[z++] = pal[v][2];
|
||||||
|
|
||||||
|
if (target == 4)
|
||||||
|
out[z++] = 255;
|
||||||
|
}
|
||||||
|
rbmp__skip(s, pad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int rshift=0;
|
||||||
|
int gshift=0;
|
||||||
|
int bshift=0;
|
||||||
|
int ashift=0;
|
||||||
|
int rcount=0;
|
||||||
|
int gcount=0;
|
||||||
|
int bcount=0;
|
||||||
|
int acount=0;
|
||||||
|
int z = 0;
|
||||||
|
int easy=0;
|
||||||
|
|
||||||
|
rbmp__skip(s, offset - 14 - hsz);
|
||||||
|
|
||||||
|
if (bpp == 24)
|
||||||
|
width = 3 * s->img_x;
|
||||||
|
else if (bpp == 16)
|
||||||
|
width = 2*s->img_x;
|
||||||
|
else /* bpp = 32 and pad = 0 */
|
||||||
|
width=0;
|
||||||
|
|
||||||
|
pad = (-width) & 3;
|
||||||
|
|
||||||
|
switch (bpp)
|
||||||
|
{
|
||||||
|
case 24:
|
||||||
|
easy = 1;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
|
||||||
|
easy = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!easy)
|
||||||
|
{
|
||||||
|
/* Corrupt BMP? */
|
||||||
|
if (!mr || !mg || !mb)
|
||||||
|
{
|
||||||
|
free(out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* right shift amt to put high bit in position #7 */
|
||||||
|
rshift = rbmp__high_bit(mr)-7;
|
||||||
|
rcount = rbmp__bitcount(mr);
|
||||||
|
gshift = rbmp__high_bit(mg)-7;
|
||||||
|
gcount = rbmp__bitcount(mg);
|
||||||
|
bshift = rbmp__high_bit(mb)-7;
|
||||||
|
bcount = rbmp__bitcount(mb);
|
||||||
|
ashift = rbmp__high_bit(ma)-7;
|
||||||
|
acount = rbmp__bitcount(ma);
|
||||||
|
}
|
||||||
|
for (j=0; j < (int) s->img_y; ++j)
|
||||||
|
{
|
||||||
|
if (easy)
|
||||||
|
{
|
||||||
|
for (i=0; i < (int) s->img_x; ++i)
|
||||||
|
{
|
||||||
|
unsigned char a;
|
||||||
|
out[z+2] = rbmp__get8(s);
|
||||||
|
out[z+1] = rbmp__get8(s);
|
||||||
|
out[z+0] = rbmp__get8(s);
|
||||||
|
z += 3;
|
||||||
|
a = (easy == 2 ? rbmp__get8(s) : 255);
|
||||||
|
if (target == 4)
|
||||||
|
out[z++] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i=0; i < (int) s->img_x; ++i)
|
||||||
|
{
|
||||||
|
uint32_t v = (bpp == 16 ? (uint32_t) rbmp__get16le(s) : rbmp__get32le(s));
|
||||||
|
int a;
|
||||||
|
out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mr, rshift, rcount));
|
||||||
|
out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mg, gshift, gcount));
|
||||||
|
out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mb, bshift, bcount));
|
||||||
|
a = (ma ? rbmp__shiftsigned(v & ma, ashift, acount) : 255);
|
||||||
|
if (target == 4)
|
||||||
|
out[z++] = RBMP__BYTECAST(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rbmp__skip(s, pad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flip_vertically)
|
||||||
|
{
|
||||||
|
unsigned char t;
|
||||||
|
for (j=0; j < (int) s->img_y>>1; ++j)
|
||||||
|
{
|
||||||
|
unsigned char *p1 = out + j *s->img_x*target;
|
||||||
|
unsigned char *p2 = out + (s->img_y-1-j)*s->img_x*target;
|
||||||
|
for (i=0; i < (int) s->img_x*target; ++i)
|
||||||
|
{
|
||||||
|
t = p1[i], p1[i] = p2[i], p2[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
req_comp
|
||||||
|
&& (req_comp >= 1 && req_comp <= 4)
|
||||||
|
&& (req_comp != target))
|
||||||
|
{
|
||||||
|
unsigned char *tmp = rbmp__convert_format(out, target, req_comp, s->img_x, s->img_y);
|
||||||
|
|
||||||
|
free(out);
|
||||||
|
out = NULL;
|
||||||
|
|
||||||
|
if (!tmp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
out = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
*x = s->img_x;
|
||||||
|
*y = s->img_y;
|
||||||
|
|
||||||
|
if (comp)
|
||||||
|
*comp = s->img_n;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *rbmp_load_from_memory(unsigned char const *buffer, int len,
|
||||||
|
unsigned *x, unsigned *y, int *comp, int req_comp)
|
||||||
|
{
|
||||||
|
rbmp__context s;
|
||||||
|
|
||||||
|
s.img_buffer = (unsigned char*)buffer;
|
||||||
|
s.img_buffer_original = (unsigned char*)buffer;
|
||||||
|
s.img_buffer_end = (unsigned char*)buffer+len;
|
||||||
|
|
||||||
|
return rbmp__bmp_load(&s,x,y,comp,req_comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rbmp_convert_frame(uint32_t *frame, unsigned width, unsigned height)
|
||||||
|
{
|
||||||
|
uint32_t *end = frame + (width * height * sizeof(uint32_t))/4;
|
||||||
|
|
||||||
|
while(frame < end)
|
||||||
|
{
|
||||||
|
uint32_t pixel = *frame;
|
||||||
|
*frame = (pixel & 0xff00ff00) | ((pixel << 16) & 0x00ff0000) | ((pixel >> 16) & 0xff);
|
||||||
|
frame++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rbmp_process_image(rbmp_t *rbmp, void **buf_data,
|
||||||
|
size_t size, unsigned *width, unsigned *height)
|
||||||
|
{
|
||||||
|
int comp;
|
||||||
|
|
||||||
|
if (!rbmp)
|
||||||
|
return IMAGE_PROCESS_ERROR;
|
||||||
|
|
||||||
|
rbmp->output_image = (uint32_t*)rbmp_load_from_memory(rbmp->buff_data,
|
||||||
|
(int)size, width, height, &comp, 4);
|
||||||
|
*buf_data = rbmp->output_image;
|
||||||
|
|
||||||
|
rbmp_convert_frame(rbmp->output_image, *width, *height);
|
||||||
|
|
||||||
|
return IMAGE_PROCESS_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rbmp_set_buf_ptr(rbmp_t *rbmp, void *data)
|
||||||
|
{
|
||||||
|
if (!rbmp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rbmp->buff_data = (uint8_t*)data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rbmp_free(rbmp_t *rbmp)
|
||||||
|
{
|
||||||
|
if (!rbmp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(rbmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
rbmp_t *rbmp_alloc(void)
|
||||||
|
{
|
||||||
|
rbmp_t *rbmp = (rbmp_t*)calloc(1, sizeof(*rbmp));
|
||||||
|
if (!rbmp)
|
||||||
|
return NULL;
|
||||||
|
return rbmp;
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rbmp_encode.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <formats/rbmp.h>
|
||||||
|
|
||||||
|
static bool write_header_bmp(RFILE *file, unsigned width, unsigned height, bool is32bpp)
|
||||||
|
{
|
||||||
|
uint8_t header[54];
|
||||||
|
unsigned line_size = (width * (is32bpp?4:3) + 3) & ~3;
|
||||||
|
unsigned size = line_size * height + 54;
|
||||||
|
unsigned size_array = line_size * height;
|
||||||
|
|
||||||
|
/* Generic BMP stuff. */
|
||||||
|
|
||||||
|
/* signature */
|
||||||
|
header[0] = 'B';
|
||||||
|
header[1] = 'M';
|
||||||
|
/* file size */
|
||||||
|
header[2] = (uint8_t)(size >> 0);
|
||||||
|
header[3] = (uint8_t)(size >> 8);
|
||||||
|
header[4] = (uint8_t)(size >> 16);
|
||||||
|
header[5] = (uint8_t)(size >> 24);
|
||||||
|
/* reserved */
|
||||||
|
header[6] = 0;
|
||||||
|
header[7] = 0;
|
||||||
|
header[8] = 0;
|
||||||
|
header[9] = 0;
|
||||||
|
/* offset */
|
||||||
|
header[10] = 54;
|
||||||
|
header[11] = 0;
|
||||||
|
header[12] = 0;
|
||||||
|
header[13] = 0;
|
||||||
|
/* DIB size */
|
||||||
|
header[14] = 40;
|
||||||
|
header[15] = 0;
|
||||||
|
header[16] = 0;
|
||||||
|
header[17] = 0;
|
||||||
|
/* Width */
|
||||||
|
header[18] = (uint8_t)(width >> 0);
|
||||||
|
header[19] = (uint8_t)(width >> 8);
|
||||||
|
header[20] = (uint8_t)(width >> 16);
|
||||||
|
header[21] = (uint8_t)(width >> 24);
|
||||||
|
/* Height */
|
||||||
|
header[22] = (uint8_t)(height >> 0);
|
||||||
|
header[23] = (uint8_t)(height >> 8);
|
||||||
|
header[24] = (uint8_t)(height >> 16);
|
||||||
|
header[25] = (uint8_t)(height >> 24);
|
||||||
|
/* Color planes */
|
||||||
|
header[26] = 1;
|
||||||
|
header[27] = 0;
|
||||||
|
/* Bits per pixel */
|
||||||
|
header[28] = is32bpp?32:24;
|
||||||
|
header[29] = 0;
|
||||||
|
/* Compression method */
|
||||||
|
header[30] = 0;
|
||||||
|
header[31] = 0;
|
||||||
|
header[32] = 0;
|
||||||
|
header[33] = 0;
|
||||||
|
/* Image data size */
|
||||||
|
header[34] = (uint8_t)(size_array >> 0);
|
||||||
|
header[35] = (uint8_t)(size_array >> 8);
|
||||||
|
header[36] = (uint8_t)(size_array >> 16);
|
||||||
|
header[37] = (uint8_t)(size_array >> 24);
|
||||||
|
/* Horizontal resolution */
|
||||||
|
header[38] = 19;
|
||||||
|
header[39] = 11;
|
||||||
|
header[40] = 0;
|
||||||
|
header[41] = 0;
|
||||||
|
/* Vertical resolution */
|
||||||
|
header[42] = 19;
|
||||||
|
header[43] = 11;
|
||||||
|
header[44] = 0;
|
||||||
|
header[45] = 0;
|
||||||
|
/* Palette size */
|
||||||
|
header[46] = 0;
|
||||||
|
header[47] = 0;
|
||||||
|
header[48] = 0;
|
||||||
|
header[49] = 0;
|
||||||
|
/* Important color count */
|
||||||
|
header[50] = 0;
|
||||||
|
header[51] = 0;
|
||||||
|
header[52] = 0;
|
||||||
|
header[53] = 0;
|
||||||
|
|
||||||
|
return filestream_write(file, header, sizeof(header)) == sizeof(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_line_565_to_24(uint8_t *line, const uint16_t *src, unsigned width)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
uint16_t pixel = *src++;
|
||||||
|
uint8_t b = (pixel >> 0) & 0x1f;
|
||||||
|
uint8_t g = (pixel >> 5) & 0x3f;
|
||||||
|
uint8_t r = (pixel >> 11) & 0x1f;
|
||||||
|
*line++ = (b << 3) | (b >> 2);
|
||||||
|
*line++ = (g << 2) | (g >> 4);
|
||||||
|
*line++ = (r << 3) | (r >> 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_line_32_to_24(uint8_t *line, const uint32_t *src, unsigned width)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
uint32_t pixel = *src++;
|
||||||
|
*line++ = (pixel >> 0) & 0xff;
|
||||||
|
*line++ = (pixel >> 8) & 0xff;
|
||||||
|
*line++ = (pixel >> 16) & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_content(RFILE *file, const void *frame,
|
||||||
|
int width, int height, int pitch, enum rbmp_source_type type)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
size_t line_size;
|
||||||
|
uint8_t *line = NULL;
|
||||||
|
int bytes_per_pixel = (type==RBMP_SOURCE_TYPE_ARGB8888?4:3);
|
||||||
|
union
|
||||||
|
{
|
||||||
|
const uint8_t *u8;
|
||||||
|
const uint16_t *u16;
|
||||||
|
const uint32_t *u32;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
u.u8 = (const uint8_t*)frame;
|
||||||
|
line_size = (width * bytes_per_pixel + 3) & ~3;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case RBMP_SOURCE_TYPE_BGR24:
|
||||||
|
{
|
||||||
|
/* BGR24 byte order input matches output. Can directly copy, but... need to make sure we pad it. */
|
||||||
|
uint32_t zeros = 0;
|
||||||
|
int pad = (int)(line_size-pitch);
|
||||||
|
for (j = 0; j < height; j++, u.u8 += pitch)
|
||||||
|
{
|
||||||
|
filestream_write(file, u.u8, pitch);
|
||||||
|
if(pad != 0)
|
||||||
|
filestream_write(file, &zeros, pad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RBMP_SOURCE_TYPE_ARGB8888:
|
||||||
|
/* ARGB8888 byte order input matches output. Can directly copy. */
|
||||||
|
for (j = 0; j < height; j++, u.u8 += pitch)
|
||||||
|
filestream_write(file, u.u8, line_size);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate line buffer, and initialize the final four bytes to zero, for deterministic padding */
|
||||||
|
line = (uint8_t*)malloc(line_size);
|
||||||
|
if (!line)
|
||||||
|
return;
|
||||||
|
*(uint32_t*)(line + line_size - 4) = 0;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case RBMP_SOURCE_TYPE_XRGB888:
|
||||||
|
for (j = 0; j < height; j++, u.u8 += pitch)
|
||||||
|
{
|
||||||
|
dump_line_32_to_24(line, u.u32, width);
|
||||||
|
filestream_write(file, line, line_size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RBMP_SOURCE_TYPE_RGB565:
|
||||||
|
for (j = 0; j < height; j++, u.u8 += pitch)
|
||||||
|
{
|
||||||
|
dump_line_565_to_24(line, u.u16, width);
|
||||||
|
filestream_write(file, line, line_size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free allocated line buffer */
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rbmp_save_image(
|
||||||
|
const char *filename,
|
||||||
|
const void *frame,
|
||||||
|
unsigned width, unsigned height,
|
||||||
|
unsigned pitch, enum rbmp_source_type type)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
RFILE *file = filestream_open(filename,
|
||||||
|
RETRO_VFS_FILE_ACCESS_WRITE,
|
||||||
|
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||||
|
if (!file)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ret = write_header_bmp(file, width, height, type==RBMP_SOURCE_TYPE_ARGB8888);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
dump_content(file, frame, width, height, pitch, type);
|
||||||
|
|
||||||
|
filestream_close(file);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,315 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (image_texture.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <boolean.h>
|
||||||
|
#include <formats/image.h>
|
||||||
|
#include <file/nbio.h>
|
||||||
|
|
||||||
|
enum video_image_format
|
||||||
|
{
|
||||||
|
IMAGE_FORMAT_NONE = 0,
|
||||||
|
IMAGE_FORMAT_TGA,
|
||||||
|
IMAGE_FORMAT_PNG,
|
||||||
|
IMAGE_FORMAT_JPEG,
|
||||||
|
IMAGE_FORMAT_BMP
|
||||||
|
};
|
||||||
|
|
||||||
|
bool image_texture_set_color_shifts(
|
||||||
|
unsigned *r_shift, unsigned *g_shift, unsigned *b_shift,
|
||||||
|
unsigned *a_shift,
|
||||||
|
struct texture_image *out_img
|
||||||
|
)
|
||||||
|
{
|
||||||
|
*a_shift = 24;
|
||||||
|
*r_shift = 16;
|
||||||
|
*g_shift = 8;
|
||||||
|
*b_shift = 0;
|
||||||
|
|
||||||
|
if (out_img->supports_rgba)
|
||||||
|
{
|
||||||
|
*r_shift = 0;
|
||||||
|
*b_shift = 16;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image_texture_color_convert(unsigned r_shift,
|
||||||
|
unsigned g_shift, unsigned b_shift, unsigned a_shift,
|
||||||
|
struct texture_image *out_img)
|
||||||
|
{
|
||||||
|
/* This is quite uncommon. */
|
||||||
|
if (a_shift != 24 || r_shift != 16 || g_shift != 8 || b_shift != 0)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint32_t num_pixels = out_img->width * out_img->height;
|
||||||
|
uint32_t *pixels = (uint32_t*)out_img->pixels;
|
||||||
|
|
||||||
|
for (i = 0; i < num_pixels; i++)
|
||||||
|
{
|
||||||
|
uint32_t col = pixels[i];
|
||||||
|
uint8_t a = (uint8_t)(col >> 24);
|
||||||
|
uint8_t r = (uint8_t)(col >> 16);
|
||||||
|
uint8_t g = (uint8_t)(col >> 8);
|
||||||
|
uint8_t b = (uint8_t)(col >> 0);
|
||||||
|
pixels[i] = (a << a_shift) |
|
||||||
|
(r << r_shift) | (g << g_shift) | (b << b_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GEKKO
|
||||||
|
|
||||||
|
#define GX_BLIT_LINE_32(off) \
|
||||||
|
{ \
|
||||||
|
unsigned x; \
|
||||||
|
const uint16_t *tmp_src = src; \
|
||||||
|
uint16_t *tmp_dst = dst; \
|
||||||
|
for (x = 0; x < width2 >> 3; x++, tmp_src += 8, tmp_dst += 32) \
|
||||||
|
{ \
|
||||||
|
tmp_dst[ 0 + off] = tmp_src[0]; \
|
||||||
|
tmp_dst[ 16 + off] = tmp_src[1]; \
|
||||||
|
tmp_dst[ 1 + off] = tmp_src[2]; \
|
||||||
|
tmp_dst[ 17 + off] = tmp_src[3]; \
|
||||||
|
tmp_dst[ 2 + off] = tmp_src[4]; \
|
||||||
|
tmp_dst[ 18 + off] = tmp_src[5]; \
|
||||||
|
tmp_dst[ 3 + off] = tmp_src[6]; \
|
||||||
|
tmp_dst[ 19 + off] = tmp_src[7]; \
|
||||||
|
} \
|
||||||
|
src += tmp_pitch; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool image_texture_internal_gx_convert_texture32(
|
||||||
|
struct texture_image *image)
|
||||||
|
{
|
||||||
|
unsigned tmp_pitch, width2, i;
|
||||||
|
const uint16_t *src = NULL;
|
||||||
|
uint16_t *dst = NULL;
|
||||||
|
/* Memory allocation in libogc is extremely primitive so try
|
||||||
|
* to avoid gaps in memory when converting by copying over to
|
||||||
|
* a temporary buffer first, then converting over into
|
||||||
|
* main buffer again. */
|
||||||
|
void *tmp = malloc(image->width
|
||||||
|
* image->height * sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (!tmp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memcpy(tmp, image->pixels, image->width
|
||||||
|
* image->height * sizeof(uint32_t));
|
||||||
|
tmp_pitch = (image->width * sizeof(uint32_t)) >> 1;
|
||||||
|
|
||||||
|
image->width &= ~3;
|
||||||
|
image->height &= ~3;
|
||||||
|
width2 = image->width << 1;
|
||||||
|
src = (uint16_t*)tmp;
|
||||||
|
dst = (uint16_t*)image->pixels;
|
||||||
|
|
||||||
|
for (i = 0; i < image->height; i += 4, dst += 4 * width2)
|
||||||
|
{
|
||||||
|
GX_BLIT_LINE_32(0)
|
||||||
|
GX_BLIT_LINE_32(4)
|
||||||
|
GX_BLIT_LINE_32(8)
|
||||||
|
GX_BLIT_LINE_32(12)
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tmp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool image_texture_load_internal(
|
||||||
|
enum image_type_enum type,
|
||||||
|
void *ptr,
|
||||||
|
size_t len,
|
||||||
|
struct texture_image *out_img,
|
||||||
|
unsigned a_shift, unsigned r_shift,
|
||||||
|
unsigned g_shift, unsigned b_shift)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
bool success = false;
|
||||||
|
void *img = image_transfer_new(type);
|
||||||
|
|
||||||
|
if (!img)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
image_transfer_set_buffer_ptr(img, type, (uint8_t*)ptr);
|
||||||
|
|
||||||
|
if (!image_transfer_start(img, type))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
while (image_transfer_iterate(img, type));
|
||||||
|
|
||||||
|
if (!image_transfer_is_valid(img, type))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = image_transfer_process(img, type,
|
||||||
|
(uint32_t**)&out_img->pixels, len, &out_img->width,
|
||||||
|
&out_img->height);
|
||||||
|
}while(ret == IMAGE_PROCESS_NEXT);
|
||||||
|
|
||||||
|
if (ret == IMAGE_PROCESS_ERROR || ret == IMAGE_PROCESS_ERROR_END)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
image_texture_color_convert(r_shift, g_shift, b_shift,
|
||||||
|
a_shift, out_img);
|
||||||
|
|
||||||
|
#ifdef GEKKO
|
||||||
|
if (!image_texture_internal_gx_convert_texture32(out_img))
|
||||||
|
{
|
||||||
|
image_texture_free(out_img);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (img)
|
||||||
|
image_transfer_free(img, type);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void image_texture_free(struct texture_image *img)
|
||||||
|
{
|
||||||
|
if (!img)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (img->pixels)
|
||||||
|
free(img->pixels);
|
||||||
|
img->width = 0;
|
||||||
|
img->height = 0;
|
||||||
|
img->pixels = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum video_image_format image_texture_get_type(const char *path)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
if (strstr(path, ".tga"))
|
||||||
|
return IMAGE_FORMAT_TGA;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
if (strstr(path, ".png"))
|
||||||
|
return IMAGE_FORMAT_PNG;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
if (strstr(path, ".jpg") || strstr(path, ".jpeg"))
|
||||||
|
return IMAGE_FORMAT_JPEG;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RBMP
|
||||||
|
if (strstr(path, ".bmp"))
|
||||||
|
return IMAGE_FORMAT_BMP;
|
||||||
|
#endif
|
||||||
|
return IMAGE_FORMAT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum image_type_enum image_texture_convert_fmt_to_type(enum video_image_format fmt)
|
||||||
|
{
|
||||||
|
switch (fmt)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
case IMAGE_FORMAT_PNG:
|
||||||
|
return IMAGE_TYPE_PNG;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
case IMAGE_FORMAT_JPEG:
|
||||||
|
return IMAGE_TYPE_JPEG;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RBMP
|
||||||
|
case IMAGE_FORMAT_BMP:
|
||||||
|
return IMAGE_TYPE_BMP;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
case IMAGE_FORMAT_TGA:
|
||||||
|
return IMAGE_TYPE_TGA;
|
||||||
|
#endif
|
||||||
|
case IMAGE_FORMAT_NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IMAGE_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image_texture_load(struct texture_image *out_img,
|
||||||
|
const char *path)
|
||||||
|
{
|
||||||
|
unsigned r_shift, g_shift, b_shift, a_shift;
|
||||||
|
size_t file_len = 0;
|
||||||
|
struct nbio_t *handle = NULL;
|
||||||
|
void *ptr = NULL;
|
||||||
|
enum video_image_format fmt = image_texture_get_type(path);
|
||||||
|
|
||||||
|
image_texture_set_color_shifts(&r_shift, &g_shift, &b_shift,
|
||||||
|
&a_shift, out_img);
|
||||||
|
|
||||||
|
if (fmt != IMAGE_FORMAT_NONE)
|
||||||
|
{
|
||||||
|
handle = (struct nbio_t*)nbio_open(path, NBIO_READ);
|
||||||
|
if (!handle)
|
||||||
|
goto error;
|
||||||
|
nbio_begin_read(handle);
|
||||||
|
|
||||||
|
while (!nbio_iterate(handle));
|
||||||
|
|
||||||
|
ptr = nbio_get_ptr(handle, &file_len);
|
||||||
|
|
||||||
|
if (!ptr)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (image_texture_load_internal(
|
||||||
|
image_texture_convert_fmt_to_type(fmt),
|
||||||
|
ptr, file_len, out_img,
|
||||||
|
a_shift, r_shift, g_shift, b_shift))
|
||||||
|
goto success;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
out_img->supports_rgba = false;
|
||||||
|
out_img->pixels = NULL;
|
||||||
|
out_img->width = 0;
|
||||||
|
out_img->height = 0;
|
||||||
|
if (handle)
|
||||||
|
nbio_free(handle);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
success:
|
||||||
|
if (handle)
|
||||||
|
nbio_free(handle);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,286 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (image_transfer.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
#include <formats/rpng.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
#include <formats/rjpeg.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
#include <formats/rtga.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RBMP
|
||||||
|
#include <formats/rbmp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <formats/image.h>
|
||||||
|
|
||||||
|
void image_transfer_free(void *data, enum image_type_enum type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IMAGE_TYPE_TGA:
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
rtga_free((rtga_t*)data);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_PNG:
|
||||||
|
{
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
rpng_t *rpng = (rpng_t*)data;
|
||||||
|
if (rpng)
|
||||||
|
rpng_free(rpng);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_JPEG:
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
rjpeg_free((rjpeg_t*)data);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_BMP:
|
||||||
|
#ifdef HAVE_RBMP
|
||||||
|
rbmp_free((rbmp_t*)data);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *image_transfer_new(enum image_type_enum type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IMAGE_TYPE_PNG:
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
return rpng_alloc();
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_JPEG:
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
return rjpeg_alloc();
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_TGA:
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
return rtga_alloc();
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_BMP:
|
||||||
|
#ifdef HAVE_RBMP
|
||||||
|
return rbmp_alloc();
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image_transfer_start(void *data, enum image_type_enum type)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IMAGE_TYPE_PNG:
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
if (!rpng_start((rpng_t*)data))
|
||||||
|
break;
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_JPEG:
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_TGA:
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_BMP:
|
||||||
|
return true;
|
||||||
|
case IMAGE_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image_transfer_is_valid(
|
||||||
|
void *data,
|
||||||
|
enum image_type_enum type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IMAGE_TYPE_PNG:
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
return rpng_is_valid((rpng_t*)data);
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_JPEG:
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_TGA:
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_BMP:
|
||||||
|
return true;
|
||||||
|
case IMAGE_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void image_transfer_set_buffer_ptr(
|
||||||
|
void *data,
|
||||||
|
enum image_type_enum type,
|
||||||
|
void *ptr)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IMAGE_TYPE_PNG:
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
rpng_set_buf_ptr((rpng_t*)data, (uint8_t*)ptr);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_JPEG:
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
rjpeg_set_buf_ptr((rjpeg_t*)data, (uint8_t*)ptr);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_TGA:
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
rtga_set_buf_ptr((rtga_t*)data, (uint8_t*)ptr);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_BMP:
|
||||||
|
#ifdef HAVE_RBMP
|
||||||
|
rbmp_set_buf_ptr((rbmp_t*)data, (uint8_t*)ptr);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int image_transfer_process(
|
||||||
|
void *data,
|
||||||
|
enum image_type_enum type,
|
||||||
|
uint32_t **buf, size_t len,
|
||||||
|
unsigned *width, unsigned *height)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IMAGE_TYPE_PNG:
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
if (!rpng_is_valid((rpng_t*)data))
|
||||||
|
return IMAGE_PROCESS_ERROR;
|
||||||
|
|
||||||
|
return rpng_process_image(
|
||||||
|
(rpng_t*)data,
|
||||||
|
(void**)buf, len, width, height);
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_JPEG:
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
return rjpeg_process_image((rjpeg_t*)data,
|
||||||
|
(void**)buf, len, width, height);
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_TGA:
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
return rtga_process_image((rtga_t*)data,
|
||||||
|
(void**)buf, len, width, height);
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_BMP:
|
||||||
|
#ifdef HAVE_RBMP
|
||||||
|
return rbmp_process_image((rbmp_t*)data,
|
||||||
|
(void**)buf, len, width, height);
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image_transfer_iterate(void *data, enum image_type_enum type)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IMAGE_TYPE_PNG:
|
||||||
|
#ifdef HAVE_RPNG
|
||||||
|
if (!rpng_iterate_image((rpng_t*)data))
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_JPEG:
|
||||||
|
#ifdef HAVE_RJPEG
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_TGA:
|
||||||
|
#ifdef HAVE_RTGA
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case IMAGE_TYPE_BMP:
|
||||||
|
return false;
|
||||||
|
case IMAGE_TYPE_NONE:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,316 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (jsonsax.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <setjmp.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <retro_inline.h>
|
||||||
|
#include <formats/jsonsax.h>
|
||||||
|
|
||||||
|
#ifdef JSONSAX_ERRORS
|
||||||
|
const char* jsonsax_errors[] =
|
||||||
|
{
|
||||||
|
"Ok",
|
||||||
|
"Interrupted",
|
||||||
|
"Missing key",
|
||||||
|
"Unterminated key",
|
||||||
|
"Missing value",
|
||||||
|
"Unterminated object",
|
||||||
|
"Unterminated array",
|
||||||
|
"Unterminated string",
|
||||||
|
"Invalid value"
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const jsonsax_handlers_t* handlers;
|
||||||
|
|
||||||
|
const char* json;
|
||||||
|
void* ud;
|
||||||
|
jmp_buf env;
|
||||||
|
}
|
||||||
|
state_t;
|
||||||
|
|
||||||
|
static INLINE void skip_spaces( state_t* state )
|
||||||
|
{
|
||||||
|
while ( isspace( (unsigned char)*state->json ) )
|
||||||
|
state->json++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void skip_digits( state_t* state )
|
||||||
|
{
|
||||||
|
while ( isdigit( (unsigned char)*state->json ) )
|
||||||
|
state->json++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HANDLE_0( event ) \
|
||||||
|
do { \
|
||||||
|
if ( state->handlers->event && state->handlers->event( state->ud ) ) \
|
||||||
|
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
||||||
|
} while ( 0 )
|
||||||
|
|
||||||
|
#define HANDLE_1( event, arg1 ) \
|
||||||
|
do { \
|
||||||
|
if ( state->handlers->event && state->handlers->event( state->ud, arg1 ) ) \
|
||||||
|
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
||||||
|
} while ( 0 )
|
||||||
|
|
||||||
|
#define HANDLE_2( event, arg1, arg2 ) \
|
||||||
|
do { \
|
||||||
|
if ( state->handlers->event && state->handlers->event( state->ud, arg1, arg2 ) ) \
|
||||||
|
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
||||||
|
} while ( 0 )
|
||||||
|
|
||||||
|
static void jsonx_parse_value(state_t* state);
|
||||||
|
|
||||||
|
static void jsonx_parse_object( state_t* state )
|
||||||
|
{
|
||||||
|
state->json++; /* we're sure the current character is a '{' */
|
||||||
|
skip_spaces( state );
|
||||||
|
HANDLE_0( start_object );
|
||||||
|
|
||||||
|
while ( *state->json != '}' )
|
||||||
|
{
|
||||||
|
const char *name = NULL;
|
||||||
|
if ( *state->json != '"' )
|
||||||
|
longjmp( state->env, JSONSAX_MISSING_KEY );
|
||||||
|
|
||||||
|
name = ++state->json;
|
||||||
|
|
||||||
|
for ( ;; )
|
||||||
|
{
|
||||||
|
const char* quote = strchr( state->json, '"' );
|
||||||
|
|
||||||
|
if ( !quote )
|
||||||
|
longjmp( state->env, JSONSAX_UNTERMINATED_KEY );
|
||||||
|
|
||||||
|
state->json = quote + 1;
|
||||||
|
|
||||||
|
if ( quote[ -1 ] != '\\' )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE_2( key, name, state->json - name - 1 );
|
||||||
|
skip_spaces( state );
|
||||||
|
|
||||||
|
if ( *state->json != ':' )
|
||||||
|
longjmp( state->env, JSONSAX_MISSING_VALUE );
|
||||||
|
|
||||||
|
state->json++;
|
||||||
|
skip_spaces( state );
|
||||||
|
jsonx_parse_value( state );
|
||||||
|
skip_spaces( state );
|
||||||
|
|
||||||
|
if ( *state->json != ',' )
|
||||||
|
break;
|
||||||
|
|
||||||
|
state->json++;
|
||||||
|
skip_spaces( state );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( *state->json != '}' )
|
||||||
|
longjmp( state->env, JSONSAX_UNTERMINATED_OBJECT );
|
||||||
|
|
||||||
|
state->json++;
|
||||||
|
HANDLE_0( end_object );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jsonx_parse_array(state_t* state)
|
||||||
|
{
|
||||||
|
unsigned int ndx = 0;
|
||||||
|
|
||||||
|
state->json++; /* we're sure the current character is a '[' */
|
||||||
|
skip_spaces( state );
|
||||||
|
HANDLE_0( start_array );
|
||||||
|
|
||||||
|
while ( *state->json != ']' )
|
||||||
|
{
|
||||||
|
HANDLE_1( array_index, ndx++ );
|
||||||
|
jsonx_parse_value( state );
|
||||||
|
skip_spaces( state );
|
||||||
|
|
||||||
|
if ( *state->json != ',' )
|
||||||
|
break;
|
||||||
|
|
||||||
|
state->json++;
|
||||||
|
skip_spaces( state );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( *state->json != ']' )
|
||||||
|
longjmp( state->env, JSONSAX_UNTERMINATED_ARRAY );
|
||||||
|
|
||||||
|
state->json++;
|
||||||
|
HANDLE_0( end_array );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jsonx_parse_string(state_t* state)
|
||||||
|
{
|
||||||
|
const char* string = ++state->json;
|
||||||
|
|
||||||
|
for ( ;; )
|
||||||
|
{
|
||||||
|
const char* quote = strchr( state->json, '"' );
|
||||||
|
|
||||||
|
if ( !quote )
|
||||||
|
longjmp( state->env, JSONSAX_UNTERMINATED_STRING );
|
||||||
|
|
||||||
|
state->json = quote + 1;
|
||||||
|
|
||||||
|
if ( quote[ -1 ] != '\\' )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE_2( string, string, state->json - string - 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jsonx_parse_boolean(state_t* state)
|
||||||
|
{
|
||||||
|
if ( !strncmp( state->json, "true", 4 ) )
|
||||||
|
{
|
||||||
|
state->json += 4;
|
||||||
|
HANDLE_1( boolean, 1 );
|
||||||
|
}
|
||||||
|
else if ( !strncmp( state->json, "false", 5 ) )
|
||||||
|
{
|
||||||
|
state->json += 5;
|
||||||
|
HANDLE_1( boolean, 0 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jsonx_parse_null(state_t* state)
|
||||||
|
{
|
||||||
|
if ( !strncmp( state->json + 1, "ull", 3 ) ) /* we're sure the current character is a 'n' */
|
||||||
|
{
|
||||||
|
state->json += 4;
|
||||||
|
HANDLE_0( null );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jsonx_parse_number(state_t* state)
|
||||||
|
{
|
||||||
|
const char* number = state->json;
|
||||||
|
|
||||||
|
if ( *state->json == '-' )
|
||||||
|
state->json++;
|
||||||
|
|
||||||
|
if ( !isdigit( (unsigned char)*state->json ) )
|
||||||
|
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||||
|
|
||||||
|
skip_digits( state );
|
||||||
|
|
||||||
|
if ( *state->json == '.' )
|
||||||
|
{
|
||||||
|
state->json++;
|
||||||
|
|
||||||
|
if ( !isdigit( (unsigned char)*state->json ) )
|
||||||
|
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||||
|
|
||||||
|
skip_digits( state );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( *state->json == 'e' || *state->json == 'E' )
|
||||||
|
{
|
||||||
|
state->json++;
|
||||||
|
|
||||||
|
if ( *state->json == '-' || *state->json == '+' )
|
||||||
|
state->json++;
|
||||||
|
|
||||||
|
if ( !isdigit( (unsigned char)*state->json ) )
|
||||||
|
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||||
|
|
||||||
|
skip_digits( state );
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE_2( number, number, state->json - number );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jsonx_parse_value(state_t* state)
|
||||||
|
{
|
||||||
|
skip_spaces( state );
|
||||||
|
|
||||||
|
switch ( *state->json )
|
||||||
|
{
|
||||||
|
case '{':
|
||||||
|
jsonx_parse_object(state);
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
jsonx_parse_array( state );
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
jsonx_parse_string( state );
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
case 'f':
|
||||||
|
jsonx_parse_boolean( state );
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
jsonx_parse_null( state );
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
case '-':
|
||||||
|
jsonx_parse_number( state );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int jsonsax_parse( const char* json, const jsonsax_handlers_t* handlers, void* userdata )
|
||||||
|
{
|
||||||
|
state_t state;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
state.json = json;
|
||||||
|
state.handlers = handlers;
|
||||||
|
state.ud = userdata;
|
||||||
|
|
||||||
|
if ( ( res = setjmp( state.env ) ) == 0 )
|
||||||
|
{
|
||||||
|
if ( handlers->start_document )
|
||||||
|
handlers->start_document( userdata );
|
||||||
|
|
||||||
|
jsonx_parse_value(&state);
|
||||||
|
|
||||||
|
if ( handlers->end_document )
|
||||||
|
handlers->end_document( userdata );
|
||||||
|
|
||||||
|
res = JSONSAX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,125 @@
|
||||||
|
/* license:BSD-3-Clause
|
||||||
|
* copyright-holders:Aaron Giles
|
||||||
|
***************************************************************************
|
||||||
|
|
||||||
|
bitstream.c
|
||||||
|
|
||||||
|
Helper classes for reading/writing at the bit level.
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <libchdr/bitstream.h>
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* INLINE FUNCTIONS
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
int bitstream_overflow(struct bitstream* bitstream) { return ((bitstream->doffset - bitstream->bits / 8) > bitstream->dlength); }
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* create_bitstream - constructor
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct bitstream* create_bitstream(const void *src, uint32_t srclength)
|
||||||
|
{
|
||||||
|
struct bitstream* bitstream = (struct bitstream*)malloc(sizeof(struct bitstream));
|
||||||
|
bitstream->buffer = 0;
|
||||||
|
bitstream->bits = 0;
|
||||||
|
bitstream->read = (const uint8_t*)src;
|
||||||
|
bitstream->doffset = 0;
|
||||||
|
bitstream->dlength = srclength;
|
||||||
|
return bitstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------
|
||||||
|
* bitstream_peek - fetch the requested number of bits
|
||||||
|
* but don't advance the input pointer
|
||||||
|
*-----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t bitstream_peek(struct bitstream* bitstream, int numbits)
|
||||||
|
{
|
||||||
|
if (numbits == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* fetch data if we need more */
|
||||||
|
if (numbits > bitstream->bits)
|
||||||
|
{
|
||||||
|
while (bitstream->bits <= 24)
|
||||||
|
{
|
||||||
|
if (bitstream->doffset < bitstream->dlength)
|
||||||
|
bitstream->buffer |= bitstream->read[bitstream->doffset] << (24 - bitstream->bits);
|
||||||
|
bitstream->doffset++;
|
||||||
|
bitstream->bits += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return the data */
|
||||||
|
return bitstream->buffer >> (32 - numbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------
|
||||||
|
* bitstream_remove - advance the input pointer by the
|
||||||
|
* specified number of bits
|
||||||
|
*-----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bitstream_remove(struct bitstream* bitstream, int numbits)
|
||||||
|
{
|
||||||
|
bitstream->buffer <<= numbits;
|
||||||
|
bitstream->bits -= numbits;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------
|
||||||
|
* bitstream_read - fetch the requested number of bits
|
||||||
|
*-----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t bitstream_read(struct bitstream* bitstream, int numbits)
|
||||||
|
{
|
||||||
|
uint32_t result = bitstream_peek(bitstream, numbits);
|
||||||
|
bitstream_remove(bitstream, numbits);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* read_offset - return the current read offset
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t bitstream_read_offset(struct bitstream* bitstream)
|
||||||
|
{
|
||||||
|
uint32_t result = bitstream->doffset;
|
||||||
|
int bits = bitstream->bits;
|
||||||
|
while (bits >= 8)
|
||||||
|
{
|
||||||
|
result--;
|
||||||
|
bits -= 8;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* flush - flush to the nearest byte
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t bitstream_flush(struct bitstream* bitstream)
|
||||||
|
{
|
||||||
|
while (bitstream->bits >= 8)
|
||||||
|
{
|
||||||
|
bitstream->doffset--;
|
||||||
|
bitstream->bits -= 8;
|
||||||
|
}
|
||||||
|
bitstream->bits = bitstream->buffer = 0;
|
||||||
|
return bitstream->doffset;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,422 @@
|
||||||
|
/* license:BSD-3-Clause
|
||||||
|
* copyright-holders:Aaron Giles
|
||||||
|
***************************************************************************
|
||||||
|
|
||||||
|
cdrom.c
|
||||||
|
|
||||||
|
Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this
|
||||||
|
|
||||||
|
****************************************************************************
|
||||||
|
|
||||||
|
IMPORTANT:
|
||||||
|
"physical" block addresses are the actual addresses on the emulated CD.
|
||||||
|
"chd" block addresses are the block addresses in the CHD file.
|
||||||
|
Because we pad each track to a 4-frame boundary, these addressing
|
||||||
|
schemes will differ after track 1!
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
#ifdef WANT_RAW_DATA_SECTOR
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_inline.h>
|
||||||
|
|
||||||
|
#include <libchdr/cdrom.h>
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
DEBUGGING
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/** @brief The verbose. */
|
||||||
|
#define VERBOSE (0)
|
||||||
|
#if VERBOSE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def LOG(x) do
|
||||||
|
*
|
||||||
|
* @brief A macro that defines log.
|
||||||
|
*
|
||||||
|
* @param x The void to process.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
|
||||||
|
*
|
||||||
|
* @brief Logerrors the given text.
|
||||||
|
*
|
||||||
|
* @param text The text.
|
||||||
|
*
|
||||||
|
* @return A CLIB_DECL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
|
||||||
|
#else
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def LOG(x);
|
||||||
|
*
|
||||||
|
* @brief A macro that defines log.
|
||||||
|
*
|
||||||
|
* @param x The void to process.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
CONSTANTS
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/** @brief offset within sector. */
|
||||||
|
#define SYNC_OFFSET 0x000
|
||||||
|
/** @brief 12 bytes. */
|
||||||
|
#define SYNC_NUM_BYTES 12
|
||||||
|
|
||||||
|
/** @brief offset within sector. */
|
||||||
|
#define MODE_OFFSET 0x00f
|
||||||
|
|
||||||
|
/** @brief offset within sector. */
|
||||||
|
#define ECC_P_OFFSET 0x81c
|
||||||
|
/** @brief 2 lots of 86. */
|
||||||
|
#define ECC_P_NUM_BYTES 86
|
||||||
|
/** @brief 24 bytes each. */
|
||||||
|
#define ECC_P_COMP 24
|
||||||
|
|
||||||
|
/** @brief The ECC q offset. */
|
||||||
|
#define ECC_Q_OFFSET (ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES)
|
||||||
|
/** @brief 2 lots of 52. */
|
||||||
|
#define ECC_Q_NUM_BYTES 52
|
||||||
|
/** @brief 43 bytes each. */
|
||||||
|
#define ECC_Q_COMP 43
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief -------------------------------------------------
|
||||||
|
* ECC lookup tables pre-calculated tables for ECC data calcs
|
||||||
|
* -------------------------------------------------.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint8_t ecclow[256] =
|
||||||
|
{
|
||||||
|
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
|
||||||
|
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
|
||||||
|
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
|
||||||
|
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
|
||||||
|
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
|
||||||
|
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
|
||||||
|
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
|
||||||
|
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
|
||||||
|
0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0x0d, 0x0f, 0x09, 0x0b, 0x05, 0x07, 0x01, 0x03,
|
||||||
|
0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23,
|
||||||
|
0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43,
|
||||||
|
0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63,
|
||||||
|
0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83,
|
||||||
|
0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3,
|
||||||
|
0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3,
|
||||||
|
0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief The ecchigh[ 256]. */
|
||||||
|
static const uint8_t ecchigh[256] =
|
||||||
|
{
|
||||||
|
0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05,
|
||||||
|
0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe,
|
||||||
|
0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee,
|
||||||
|
0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15,
|
||||||
|
0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce,
|
||||||
|
0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35,
|
||||||
|
0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25,
|
||||||
|
0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde,
|
||||||
|
0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e,
|
||||||
|
0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75,
|
||||||
|
0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65,
|
||||||
|
0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e,
|
||||||
|
0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45,
|
||||||
|
0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe,
|
||||||
|
0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae,
|
||||||
|
0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief -------------------------------------------------
|
||||||
|
* poffsets - each row represents the addresses used to calculate a byte of the ECC P
|
||||||
|
* data 86 (*2) ECC P bytes, 24 values represented by each
|
||||||
|
* -------------------------------------------------.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
|
||||||
|
{
|
||||||
|
{ 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba },
|
||||||
|
{ 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb },
|
||||||
|
{ 0x002,0x058,0x0ae,0x104,0x15a,0x1b0,0x206,0x25c,0x2b2,0x308,0x35e,0x3b4,0x40a,0x460,0x4b6,0x50c,0x562,0x5b8,0x60e,0x664,0x6ba,0x710,0x766,0x7bc },
|
||||||
|
{ 0x003,0x059,0x0af,0x105,0x15b,0x1b1,0x207,0x25d,0x2b3,0x309,0x35f,0x3b5,0x40b,0x461,0x4b7,0x50d,0x563,0x5b9,0x60f,0x665,0x6bb,0x711,0x767,0x7bd },
|
||||||
|
{ 0x004,0x05a,0x0b0,0x106,0x15c,0x1b2,0x208,0x25e,0x2b4,0x30a,0x360,0x3b6,0x40c,0x462,0x4b8,0x50e,0x564,0x5ba,0x610,0x666,0x6bc,0x712,0x768,0x7be },
|
||||||
|
{ 0x005,0x05b,0x0b1,0x107,0x15d,0x1b3,0x209,0x25f,0x2b5,0x30b,0x361,0x3b7,0x40d,0x463,0x4b9,0x50f,0x565,0x5bb,0x611,0x667,0x6bd,0x713,0x769,0x7bf },
|
||||||
|
{ 0x006,0x05c,0x0b2,0x108,0x15e,0x1b4,0x20a,0x260,0x2b6,0x30c,0x362,0x3b8,0x40e,0x464,0x4ba,0x510,0x566,0x5bc,0x612,0x668,0x6be,0x714,0x76a,0x7c0 },
|
||||||
|
{ 0x007,0x05d,0x0b3,0x109,0x15f,0x1b5,0x20b,0x261,0x2b7,0x30d,0x363,0x3b9,0x40f,0x465,0x4bb,0x511,0x567,0x5bd,0x613,0x669,0x6bf,0x715,0x76b,0x7c1 },
|
||||||
|
{ 0x008,0x05e,0x0b4,0x10a,0x160,0x1b6,0x20c,0x262,0x2b8,0x30e,0x364,0x3ba,0x410,0x466,0x4bc,0x512,0x568,0x5be,0x614,0x66a,0x6c0,0x716,0x76c,0x7c2 },
|
||||||
|
{ 0x009,0x05f,0x0b5,0x10b,0x161,0x1b7,0x20d,0x263,0x2b9,0x30f,0x365,0x3bb,0x411,0x467,0x4bd,0x513,0x569,0x5bf,0x615,0x66b,0x6c1,0x717,0x76d,0x7c3 },
|
||||||
|
{ 0x00a,0x060,0x0b6,0x10c,0x162,0x1b8,0x20e,0x264,0x2ba,0x310,0x366,0x3bc,0x412,0x468,0x4be,0x514,0x56a,0x5c0,0x616,0x66c,0x6c2,0x718,0x76e,0x7c4 },
|
||||||
|
{ 0x00b,0x061,0x0b7,0x10d,0x163,0x1b9,0x20f,0x265,0x2bb,0x311,0x367,0x3bd,0x413,0x469,0x4bf,0x515,0x56b,0x5c1,0x617,0x66d,0x6c3,0x719,0x76f,0x7c5 },
|
||||||
|
{ 0x00c,0x062,0x0b8,0x10e,0x164,0x1ba,0x210,0x266,0x2bc,0x312,0x368,0x3be,0x414,0x46a,0x4c0,0x516,0x56c,0x5c2,0x618,0x66e,0x6c4,0x71a,0x770,0x7c6 },
|
||||||
|
{ 0x00d,0x063,0x0b9,0x10f,0x165,0x1bb,0x211,0x267,0x2bd,0x313,0x369,0x3bf,0x415,0x46b,0x4c1,0x517,0x56d,0x5c3,0x619,0x66f,0x6c5,0x71b,0x771,0x7c7 },
|
||||||
|
{ 0x00e,0x064,0x0ba,0x110,0x166,0x1bc,0x212,0x268,0x2be,0x314,0x36a,0x3c0,0x416,0x46c,0x4c2,0x518,0x56e,0x5c4,0x61a,0x670,0x6c6,0x71c,0x772,0x7c8 },
|
||||||
|
{ 0x00f,0x065,0x0bb,0x111,0x167,0x1bd,0x213,0x269,0x2bf,0x315,0x36b,0x3c1,0x417,0x46d,0x4c3,0x519,0x56f,0x5c5,0x61b,0x671,0x6c7,0x71d,0x773,0x7c9 },
|
||||||
|
{ 0x010,0x066,0x0bc,0x112,0x168,0x1be,0x214,0x26a,0x2c0,0x316,0x36c,0x3c2,0x418,0x46e,0x4c4,0x51a,0x570,0x5c6,0x61c,0x672,0x6c8,0x71e,0x774,0x7ca },
|
||||||
|
{ 0x011,0x067,0x0bd,0x113,0x169,0x1bf,0x215,0x26b,0x2c1,0x317,0x36d,0x3c3,0x419,0x46f,0x4c5,0x51b,0x571,0x5c7,0x61d,0x673,0x6c9,0x71f,0x775,0x7cb },
|
||||||
|
{ 0x012,0x068,0x0be,0x114,0x16a,0x1c0,0x216,0x26c,0x2c2,0x318,0x36e,0x3c4,0x41a,0x470,0x4c6,0x51c,0x572,0x5c8,0x61e,0x674,0x6ca,0x720,0x776,0x7cc },
|
||||||
|
{ 0x013,0x069,0x0bf,0x115,0x16b,0x1c1,0x217,0x26d,0x2c3,0x319,0x36f,0x3c5,0x41b,0x471,0x4c7,0x51d,0x573,0x5c9,0x61f,0x675,0x6cb,0x721,0x777,0x7cd },
|
||||||
|
{ 0x014,0x06a,0x0c0,0x116,0x16c,0x1c2,0x218,0x26e,0x2c4,0x31a,0x370,0x3c6,0x41c,0x472,0x4c8,0x51e,0x574,0x5ca,0x620,0x676,0x6cc,0x722,0x778,0x7ce },
|
||||||
|
{ 0x015,0x06b,0x0c1,0x117,0x16d,0x1c3,0x219,0x26f,0x2c5,0x31b,0x371,0x3c7,0x41d,0x473,0x4c9,0x51f,0x575,0x5cb,0x621,0x677,0x6cd,0x723,0x779,0x7cf },
|
||||||
|
{ 0x016,0x06c,0x0c2,0x118,0x16e,0x1c4,0x21a,0x270,0x2c6,0x31c,0x372,0x3c8,0x41e,0x474,0x4ca,0x520,0x576,0x5cc,0x622,0x678,0x6ce,0x724,0x77a,0x7d0 },
|
||||||
|
{ 0x017,0x06d,0x0c3,0x119,0x16f,0x1c5,0x21b,0x271,0x2c7,0x31d,0x373,0x3c9,0x41f,0x475,0x4cb,0x521,0x577,0x5cd,0x623,0x679,0x6cf,0x725,0x77b,0x7d1 },
|
||||||
|
{ 0x018,0x06e,0x0c4,0x11a,0x170,0x1c6,0x21c,0x272,0x2c8,0x31e,0x374,0x3ca,0x420,0x476,0x4cc,0x522,0x578,0x5ce,0x624,0x67a,0x6d0,0x726,0x77c,0x7d2 },
|
||||||
|
{ 0x019,0x06f,0x0c5,0x11b,0x171,0x1c7,0x21d,0x273,0x2c9,0x31f,0x375,0x3cb,0x421,0x477,0x4cd,0x523,0x579,0x5cf,0x625,0x67b,0x6d1,0x727,0x77d,0x7d3 },
|
||||||
|
{ 0x01a,0x070,0x0c6,0x11c,0x172,0x1c8,0x21e,0x274,0x2ca,0x320,0x376,0x3cc,0x422,0x478,0x4ce,0x524,0x57a,0x5d0,0x626,0x67c,0x6d2,0x728,0x77e,0x7d4 },
|
||||||
|
{ 0x01b,0x071,0x0c7,0x11d,0x173,0x1c9,0x21f,0x275,0x2cb,0x321,0x377,0x3cd,0x423,0x479,0x4cf,0x525,0x57b,0x5d1,0x627,0x67d,0x6d3,0x729,0x77f,0x7d5 },
|
||||||
|
{ 0x01c,0x072,0x0c8,0x11e,0x174,0x1ca,0x220,0x276,0x2cc,0x322,0x378,0x3ce,0x424,0x47a,0x4d0,0x526,0x57c,0x5d2,0x628,0x67e,0x6d4,0x72a,0x780,0x7d6 },
|
||||||
|
{ 0x01d,0x073,0x0c9,0x11f,0x175,0x1cb,0x221,0x277,0x2cd,0x323,0x379,0x3cf,0x425,0x47b,0x4d1,0x527,0x57d,0x5d3,0x629,0x67f,0x6d5,0x72b,0x781,0x7d7 },
|
||||||
|
{ 0x01e,0x074,0x0ca,0x120,0x176,0x1cc,0x222,0x278,0x2ce,0x324,0x37a,0x3d0,0x426,0x47c,0x4d2,0x528,0x57e,0x5d4,0x62a,0x680,0x6d6,0x72c,0x782,0x7d8 },
|
||||||
|
{ 0x01f,0x075,0x0cb,0x121,0x177,0x1cd,0x223,0x279,0x2cf,0x325,0x37b,0x3d1,0x427,0x47d,0x4d3,0x529,0x57f,0x5d5,0x62b,0x681,0x6d7,0x72d,0x783,0x7d9 },
|
||||||
|
{ 0x020,0x076,0x0cc,0x122,0x178,0x1ce,0x224,0x27a,0x2d0,0x326,0x37c,0x3d2,0x428,0x47e,0x4d4,0x52a,0x580,0x5d6,0x62c,0x682,0x6d8,0x72e,0x784,0x7da },
|
||||||
|
{ 0x021,0x077,0x0cd,0x123,0x179,0x1cf,0x225,0x27b,0x2d1,0x327,0x37d,0x3d3,0x429,0x47f,0x4d5,0x52b,0x581,0x5d7,0x62d,0x683,0x6d9,0x72f,0x785,0x7db },
|
||||||
|
{ 0x022,0x078,0x0ce,0x124,0x17a,0x1d0,0x226,0x27c,0x2d2,0x328,0x37e,0x3d4,0x42a,0x480,0x4d6,0x52c,0x582,0x5d8,0x62e,0x684,0x6da,0x730,0x786,0x7dc },
|
||||||
|
{ 0x023,0x079,0x0cf,0x125,0x17b,0x1d1,0x227,0x27d,0x2d3,0x329,0x37f,0x3d5,0x42b,0x481,0x4d7,0x52d,0x583,0x5d9,0x62f,0x685,0x6db,0x731,0x787,0x7dd },
|
||||||
|
{ 0x024,0x07a,0x0d0,0x126,0x17c,0x1d2,0x228,0x27e,0x2d4,0x32a,0x380,0x3d6,0x42c,0x482,0x4d8,0x52e,0x584,0x5da,0x630,0x686,0x6dc,0x732,0x788,0x7de },
|
||||||
|
{ 0x025,0x07b,0x0d1,0x127,0x17d,0x1d3,0x229,0x27f,0x2d5,0x32b,0x381,0x3d7,0x42d,0x483,0x4d9,0x52f,0x585,0x5db,0x631,0x687,0x6dd,0x733,0x789,0x7df },
|
||||||
|
{ 0x026,0x07c,0x0d2,0x128,0x17e,0x1d4,0x22a,0x280,0x2d6,0x32c,0x382,0x3d8,0x42e,0x484,0x4da,0x530,0x586,0x5dc,0x632,0x688,0x6de,0x734,0x78a,0x7e0 },
|
||||||
|
{ 0x027,0x07d,0x0d3,0x129,0x17f,0x1d5,0x22b,0x281,0x2d7,0x32d,0x383,0x3d9,0x42f,0x485,0x4db,0x531,0x587,0x5dd,0x633,0x689,0x6df,0x735,0x78b,0x7e1 },
|
||||||
|
{ 0x028,0x07e,0x0d4,0x12a,0x180,0x1d6,0x22c,0x282,0x2d8,0x32e,0x384,0x3da,0x430,0x486,0x4dc,0x532,0x588,0x5de,0x634,0x68a,0x6e0,0x736,0x78c,0x7e2 },
|
||||||
|
{ 0x029,0x07f,0x0d5,0x12b,0x181,0x1d7,0x22d,0x283,0x2d9,0x32f,0x385,0x3db,0x431,0x487,0x4dd,0x533,0x589,0x5df,0x635,0x68b,0x6e1,0x737,0x78d,0x7e3 },
|
||||||
|
{ 0x02a,0x080,0x0d6,0x12c,0x182,0x1d8,0x22e,0x284,0x2da,0x330,0x386,0x3dc,0x432,0x488,0x4de,0x534,0x58a,0x5e0,0x636,0x68c,0x6e2,0x738,0x78e,0x7e4 },
|
||||||
|
{ 0x02b,0x081,0x0d7,0x12d,0x183,0x1d9,0x22f,0x285,0x2db,0x331,0x387,0x3dd,0x433,0x489,0x4df,0x535,0x58b,0x5e1,0x637,0x68d,0x6e3,0x739,0x78f,0x7e5 },
|
||||||
|
{ 0x02c,0x082,0x0d8,0x12e,0x184,0x1da,0x230,0x286,0x2dc,0x332,0x388,0x3de,0x434,0x48a,0x4e0,0x536,0x58c,0x5e2,0x638,0x68e,0x6e4,0x73a,0x790,0x7e6 },
|
||||||
|
{ 0x02d,0x083,0x0d9,0x12f,0x185,0x1db,0x231,0x287,0x2dd,0x333,0x389,0x3df,0x435,0x48b,0x4e1,0x537,0x58d,0x5e3,0x639,0x68f,0x6e5,0x73b,0x791,0x7e7 },
|
||||||
|
{ 0x02e,0x084,0x0da,0x130,0x186,0x1dc,0x232,0x288,0x2de,0x334,0x38a,0x3e0,0x436,0x48c,0x4e2,0x538,0x58e,0x5e4,0x63a,0x690,0x6e6,0x73c,0x792,0x7e8 },
|
||||||
|
{ 0x02f,0x085,0x0db,0x131,0x187,0x1dd,0x233,0x289,0x2df,0x335,0x38b,0x3e1,0x437,0x48d,0x4e3,0x539,0x58f,0x5e5,0x63b,0x691,0x6e7,0x73d,0x793,0x7e9 },
|
||||||
|
{ 0x030,0x086,0x0dc,0x132,0x188,0x1de,0x234,0x28a,0x2e0,0x336,0x38c,0x3e2,0x438,0x48e,0x4e4,0x53a,0x590,0x5e6,0x63c,0x692,0x6e8,0x73e,0x794,0x7ea },
|
||||||
|
{ 0x031,0x087,0x0dd,0x133,0x189,0x1df,0x235,0x28b,0x2e1,0x337,0x38d,0x3e3,0x439,0x48f,0x4e5,0x53b,0x591,0x5e7,0x63d,0x693,0x6e9,0x73f,0x795,0x7eb },
|
||||||
|
{ 0x032,0x088,0x0de,0x134,0x18a,0x1e0,0x236,0x28c,0x2e2,0x338,0x38e,0x3e4,0x43a,0x490,0x4e6,0x53c,0x592,0x5e8,0x63e,0x694,0x6ea,0x740,0x796,0x7ec },
|
||||||
|
{ 0x033,0x089,0x0df,0x135,0x18b,0x1e1,0x237,0x28d,0x2e3,0x339,0x38f,0x3e5,0x43b,0x491,0x4e7,0x53d,0x593,0x5e9,0x63f,0x695,0x6eb,0x741,0x797,0x7ed },
|
||||||
|
{ 0x034,0x08a,0x0e0,0x136,0x18c,0x1e2,0x238,0x28e,0x2e4,0x33a,0x390,0x3e6,0x43c,0x492,0x4e8,0x53e,0x594,0x5ea,0x640,0x696,0x6ec,0x742,0x798,0x7ee },
|
||||||
|
{ 0x035,0x08b,0x0e1,0x137,0x18d,0x1e3,0x239,0x28f,0x2e5,0x33b,0x391,0x3e7,0x43d,0x493,0x4e9,0x53f,0x595,0x5eb,0x641,0x697,0x6ed,0x743,0x799,0x7ef },
|
||||||
|
{ 0x036,0x08c,0x0e2,0x138,0x18e,0x1e4,0x23a,0x290,0x2e6,0x33c,0x392,0x3e8,0x43e,0x494,0x4ea,0x540,0x596,0x5ec,0x642,0x698,0x6ee,0x744,0x79a,0x7f0 },
|
||||||
|
{ 0x037,0x08d,0x0e3,0x139,0x18f,0x1e5,0x23b,0x291,0x2e7,0x33d,0x393,0x3e9,0x43f,0x495,0x4eb,0x541,0x597,0x5ed,0x643,0x699,0x6ef,0x745,0x79b,0x7f1 },
|
||||||
|
{ 0x038,0x08e,0x0e4,0x13a,0x190,0x1e6,0x23c,0x292,0x2e8,0x33e,0x394,0x3ea,0x440,0x496,0x4ec,0x542,0x598,0x5ee,0x644,0x69a,0x6f0,0x746,0x79c,0x7f2 },
|
||||||
|
{ 0x039,0x08f,0x0e5,0x13b,0x191,0x1e7,0x23d,0x293,0x2e9,0x33f,0x395,0x3eb,0x441,0x497,0x4ed,0x543,0x599,0x5ef,0x645,0x69b,0x6f1,0x747,0x79d,0x7f3 },
|
||||||
|
{ 0x03a,0x090,0x0e6,0x13c,0x192,0x1e8,0x23e,0x294,0x2ea,0x340,0x396,0x3ec,0x442,0x498,0x4ee,0x544,0x59a,0x5f0,0x646,0x69c,0x6f2,0x748,0x79e,0x7f4 },
|
||||||
|
{ 0x03b,0x091,0x0e7,0x13d,0x193,0x1e9,0x23f,0x295,0x2eb,0x341,0x397,0x3ed,0x443,0x499,0x4ef,0x545,0x59b,0x5f1,0x647,0x69d,0x6f3,0x749,0x79f,0x7f5 },
|
||||||
|
{ 0x03c,0x092,0x0e8,0x13e,0x194,0x1ea,0x240,0x296,0x2ec,0x342,0x398,0x3ee,0x444,0x49a,0x4f0,0x546,0x59c,0x5f2,0x648,0x69e,0x6f4,0x74a,0x7a0,0x7f6 },
|
||||||
|
{ 0x03d,0x093,0x0e9,0x13f,0x195,0x1eb,0x241,0x297,0x2ed,0x343,0x399,0x3ef,0x445,0x49b,0x4f1,0x547,0x59d,0x5f3,0x649,0x69f,0x6f5,0x74b,0x7a1,0x7f7 },
|
||||||
|
{ 0x03e,0x094,0x0ea,0x140,0x196,0x1ec,0x242,0x298,0x2ee,0x344,0x39a,0x3f0,0x446,0x49c,0x4f2,0x548,0x59e,0x5f4,0x64a,0x6a0,0x6f6,0x74c,0x7a2,0x7f8 },
|
||||||
|
{ 0x03f,0x095,0x0eb,0x141,0x197,0x1ed,0x243,0x299,0x2ef,0x345,0x39b,0x3f1,0x447,0x49d,0x4f3,0x549,0x59f,0x5f5,0x64b,0x6a1,0x6f7,0x74d,0x7a3,0x7f9 },
|
||||||
|
{ 0x040,0x096,0x0ec,0x142,0x198,0x1ee,0x244,0x29a,0x2f0,0x346,0x39c,0x3f2,0x448,0x49e,0x4f4,0x54a,0x5a0,0x5f6,0x64c,0x6a2,0x6f8,0x74e,0x7a4,0x7fa },
|
||||||
|
{ 0x041,0x097,0x0ed,0x143,0x199,0x1ef,0x245,0x29b,0x2f1,0x347,0x39d,0x3f3,0x449,0x49f,0x4f5,0x54b,0x5a1,0x5f7,0x64d,0x6a3,0x6f9,0x74f,0x7a5,0x7fb },
|
||||||
|
{ 0x042,0x098,0x0ee,0x144,0x19a,0x1f0,0x246,0x29c,0x2f2,0x348,0x39e,0x3f4,0x44a,0x4a0,0x4f6,0x54c,0x5a2,0x5f8,0x64e,0x6a4,0x6fa,0x750,0x7a6,0x7fc },
|
||||||
|
{ 0x043,0x099,0x0ef,0x145,0x19b,0x1f1,0x247,0x29d,0x2f3,0x349,0x39f,0x3f5,0x44b,0x4a1,0x4f7,0x54d,0x5a3,0x5f9,0x64f,0x6a5,0x6fb,0x751,0x7a7,0x7fd },
|
||||||
|
{ 0x044,0x09a,0x0f0,0x146,0x19c,0x1f2,0x248,0x29e,0x2f4,0x34a,0x3a0,0x3f6,0x44c,0x4a2,0x4f8,0x54e,0x5a4,0x5fa,0x650,0x6a6,0x6fc,0x752,0x7a8,0x7fe },
|
||||||
|
{ 0x045,0x09b,0x0f1,0x147,0x19d,0x1f3,0x249,0x29f,0x2f5,0x34b,0x3a1,0x3f7,0x44d,0x4a3,0x4f9,0x54f,0x5a5,0x5fb,0x651,0x6a7,0x6fd,0x753,0x7a9,0x7ff },
|
||||||
|
{ 0x046,0x09c,0x0f2,0x148,0x19e,0x1f4,0x24a,0x2a0,0x2f6,0x34c,0x3a2,0x3f8,0x44e,0x4a4,0x4fa,0x550,0x5a6,0x5fc,0x652,0x6a8,0x6fe,0x754,0x7aa,0x800 },
|
||||||
|
{ 0x047,0x09d,0x0f3,0x149,0x19f,0x1f5,0x24b,0x2a1,0x2f7,0x34d,0x3a3,0x3f9,0x44f,0x4a5,0x4fb,0x551,0x5a7,0x5fd,0x653,0x6a9,0x6ff,0x755,0x7ab,0x801 },
|
||||||
|
{ 0x048,0x09e,0x0f4,0x14a,0x1a0,0x1f6,0x24c,0x2a2,0x2f8,0x34e,0x3a4,0x3fa,0x450,0x4a6,0x4fc,0x552,0x5a8,0x5fe,0x654,0x6aa,0x700,0x756,0x7ac,0x802 },
|
||||||
|
{ 0x049,0x09f,0x0f5,0x14b,0x1a1,0x1f7,0x24d,0x2a3,0x2f9,0x34f,0x3a5,0x3fb,0x451,0x4a7,0x4fd,0x553,0x5a9,0x5ff,0x655,0x6ab,0x701,0x757,0x7ad,0x803 },
|
||||||
|
{ 0x04a,0x0a0,0x0f6,0x14c,0x1a2,0x1f8,0x24e,0x2a4,0x2fa,0x350,0x3a6,0x3fc,0x452,0x4a8,0x4fe,0x554,0x5aa,0x600,0x656,0x6ac,0x702,0x758,0x7ae,0x804 },
|
||||||
|
{ 0x04b,0x0a1,0x0f7,0x14d,0x1a3,0x1f9,0x24f,0x2a5,0x2fb,0x351,0x3a7,0x3fd,0x453,0x4a9,0x4ff,0x555,0x5ab,0x601,0x657,0x6ad,0x703,0x759,0x7af,0x805 },
|
||||||
|
{ 0x04c,0x0a2,0x0f8,0x14e,0x1a4,0x1fa,0x250,0x2a6,0x2fc,0x352,0x3a8,0x3fe,0x454,0x4aa,0x500,0x556,0x5ac,0x602,0x658,0x6ae,0x704,0x75a,0x7b0,0x806 },
|
||||||
|
{ 0x04d,0x0a3,0x0f9,0x14f,0x1a5,0x1fb,0x251,0x2a7,0x2fd,0x353,0x3a9,0x3ff,0x455,0x4ab,0x501,0x557,0x5ad,0x603,0x659,0x6af,0x705,0x75b,0x7b1,0x807 },
|
||||||
|
{ 0x04e,0x0a4,0x0fa,0x150,0x1a6,0x1fc,0x252,0x2a8,0x2fe,0x354,0x3aa,0x400,0x456,0x4ac,0x502,0x558,0x5ae,0x604,0x65a,0x6b0,0x706,0x75c,0x7b2,0x808 },
|
||||||
|
{ 0x04f,0x0a5,0x0fb,0x151,0x1a7,0x1fd,0x253,0x2a9,0x2ff,0x355,0x3ab,0x401,0x457,0x4ad,0x503,0x559,0x5af,0x605,0x65b,0x6b1,0x707,0x75d,0x7b3,0x809 },
|
||||||
|
{ 0x050,0x0a6,0x0fc,0x152,0x1a8,0x1fe,0x254,0x2aa,0x300,0x356,0x3ac,0x402,0x458,0x4ae,0x504,0x55a,0x5b0,0x606,0x65c,0x6b2,0x708,0x75e,0x7b4,0x80a },
|
||||||
|
{ 0x051,0x0a7,0x0fd,0x153,0x1a9,0x1ff,0x255,0x2ab,0x301,0x357,0x3ad,0x403,0x459,0x4af,0x505,0x55b,0x5b1,0x607,0x65d,0x6b3,0x709,0x75f,0x7b5,0x80b },
|
||||||
|
{ 0x052,0x0a8,0x0fe,0x154,0x1aa,0x200,0x256,0x2ac,0x302,0x358,0x3ae,0x404,0x45a,0x4b0,0x506,0x55c,0x5b2,0x608,0x65e,0x6b4,0x70a,0x760,0x7b6,0x80c },
|
||||||
|
{ 0x053,0x0a9,0x0ff,0x155,0x1ab,0x201,0x257,0x2ad,0x303,0x359,0x3af,0x405,0x45b,0x4b1,0x507,0x55d,0x5b3,0x609,0x65f,0x6b5,0x70b,0x761,0x7b7,0x80d },
|
||||||
|
{ 0x054,0x0aa,0x100,0x156,0x1ac,0x202,0x258,0x2ae,0x304,0x35a,0x3b0,0x406,0x45c,0x4b2,0x508,0x55e,0x5b4,0x60a,0x660,0x6b6,0x70c,0x762,0x7b8,0x80e },
|
||||||
|
{ 0x055,0x0ab,0x101,0x157,0x1ad,0x203,0x259,0x2af,0x305,0x35b,0x3b1,0x407,0x45d,0x4b3,0x509,0x55f,0x5b5,0x60b,0x661,0x6b7,0x70d,0x763,0x7b9,0x80f }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief -------------------------------------------------
|
||||||
|
* qoffsets - each row represents the addresses used to calculate a byte of the ECC Q
|
||||||
|
* data 52 (*2) ECC Q bytes, 43 values represented by each
|
||||||
|
* -------------------------------------------------.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
|
||||||
|
{
|
||||||
|
{ 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 },
|
||||||
|
{ 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 },
|
||||||
|
{ 0x056,0x0ae,0x106,0x15e,0x1b6,0x20e,0x266,0x2be,0x316,0x36e,0x3c6,0x41e,0x476,0x4ce,0x526,0x57e,0x5d6,0x62e,0x686,0x6de,0x736,0x78e,0x7e6,0x83e,0x896,0x032,0x08a,0x0e2,0x13a,0x192,0x1ea,0x242,0x29a,0x2f2,0x34a,0x3a2,0x3fa,0x452,0x4aa,0x502,0x55a,0x5b2,0x60a },
|
||||||
|
{ 0x057,0x0af,0x107,0x15f,0x1b7,0x20f,0x267,0x2bf,0x317,0x36f,0x3c7,0x41f,0x477,0x4cf,0x527,0x57f,0x5d7,0x62f,0x687,0x6df,0x737,0x78f,0x7e7,0x83f,0x897,0x033,0x08b,0x0e3,0x13b,0x193,0x1eb,0x243,0x29b,0x2f3,0x34b,0x3a3,0x3fb,0x453,0x4ab,0x503,0x55b,0x5b3,0x60b },
|
||||||
|
{ 0x0ac,0x104,0x15c,0x1b4,0x20c,0x264,0x2bc,0x314,0x36c,0x3c4,0x41c,0x474,0x4cc,0x524,0x57c,0x5d4,0x62c,0x684,0x6dc,0x734,0x78c,0x7e4,0x83c,0x894,0x030,0x088,0x0e0,0x138,0x190,0x1e8,0x240,0x298,0x2f0,0x348,0x3a0,0x3f8,0x450,0x4a8,0x500,0x558,0x5b0,0x608,0x660 },
|
||||||
|
{ 0x0ad,0x105,0x15d,0x1b5,0x20d,0x265,0x2bd,0x315,0x36d,0x3c5,0x41d,0x475,0x4cd,0x525,0x57d,0x5d5,0x62d,0x685,0x6dd,0x735,0x78d,0x7e5,0x83d,0x895,0x031,0x089,0x0e1,0x139,0x191,0x1e9,0x241,0x299,0x2f1,0x349,0x3a1,0x3f9,0x451,0x4a9,0x501,0x559,0x5b1,0x609,0x661 },
|
||||||
|
{ 0x102,0x15a,0x1b2,0x20a,0x262,0x2ba,0x312,0x36a,0x3c2,0x41a,0x472,0x4ca,0x522,0x57a,0x5d2,0x62a,0x682,0x6da,0x732,0x78a,0x7e2,0x83a,0x892,0x02e,0x086,0x0de,0x136,0x18e,0x1e6,0x23e,0x296,0x2ee,0x346,0x39e,0x3f6,0x44e,0x4a6,0x4fe,0x556,0x5ae,0x606,0x65e,0x6b6 },
|
||||||
|
{ 0x103,0x15b,0x1b3,0x20b,0x263,0x2bb,0x313,0x36b,0x3c3,0x41b,0x473,0x4cb,0x523,0x57b,0x5d3,0x62b,0x683,0x6db,0x733,0x78b,0x7e3,0x83b,0x893,0x02f,0x087,0x0df,0x137,0x18f,0x1e7,0x23f,0x297,0x2ef,0x347,0x39f,0x3f7,0x44f,0x4a7,0x4ff,0x557,0x5af,0x607,0x65f,0x6b7 },
|
||||||
|
{ 0x158,0x1b0,0x208,0x260,0x2b8,0x310,0x368,0x3c0,0x418,0x470,0x4c8,0x520,0x578,0x5d0,0x628,0x680,0x6d8,0x730,0x788,0x7e0,0x838,0x890,0x02c,0x084,0x0dc,0x134,0x18c,0x1e4,0x23c,0x294,0x2ec,0x344,0x39c,0x3f4,0x44c,0x4a4,0x4fc,0x554,0x5ac,0x604,0x65c,0x6b4,0x70c },
|
||||||
|
{ 0x159,0x1b1,0x209,0x261,0x2b9,0x311,0x369,0x3c1,0x419,0x471,0x4c9,0x521,0x579,0x5d1,0x629,0x681,0x6d9,0x731,0x789,0x7e1,0x839,0x891,0x02d,0x085,0x0dd,0x135,0x18d,0x1e5,0x23d,0x295,0x2ed,0x345,0x39d,0x3f5,0x44d,0x4a5,0x4fd,0x555,0x5ad,0x605,0x65d,0x6b5,0x70d },
|
||||||
|
{ 0x1ae,0x206,0x25e,0x2b6,0x30e,0x366,0x3be,0x416,0x46e,0x4c6,0x51e,0x576,0x5ce,0x626,0x67e,0x6d6,0x72e,0x786,0x7de,0x836,0x88e,0x02a,0x082,0x0da,0x132,0x18a,0x1e2,0x23a,0x292,0x2ea,0x342,0x39a,0x3f2,0x44a,0x4a2,0x4fa,0x552,0x5aa,0x602,0x65a,0x6b2,0x70a,0x762 },
|
||||||
|
{ 0x1af,0x207,0x25f,0x2b7,0x30f,0x367,0x3bf,0x417,0x46f,0x4c7,0x51f,0x577,0x5cf,0x627,0x67f,0x6d7,0x72f,0x787,0x7df,0x837,0x88f,0x02b,0x083,0x0db,0x133,0x18b,0x1e3,0x23b,0x293,0x2eb,0x343,0x39b,0x3f3,0x44b,0x4a3,0x4fb,0x553,0x5ab,0x603,0x65b,0x6b3,0x70b,0x763 },
|
||||||
|
{ 0x204,0x25c,0x2b4,0x30c,0x364,0x3bc,0x414,0x46c,0x4c4,0x51c,0x574,0x5cc,0x624,0x67c,0x6d4,0x72c,0x784,0x7dc,0x834,0x88c,0x028,0x080,0x0d8,0x130,0x188,0x1e0,0x238,0x290,0x2e8,0x340,0x398,0x3f0,0x448,0x4a0,0x4f8,0x550,0x5a8,0x600,0x658,0x6b0,0x708,0x760,0x7b8 },
|
||||||
|
{ 0x205,0x25d,0x2b5,0x30d,0x365,0x3bd,0x415,0x46d,0x4c5,0x51d,0x575,0x5cd,0x625,0x67d,0x6d5,0x72d,0x785,0x7dd,0x835,0x88d,0x029,0x081,0x0d9,0x131,0x189,0x1e1,0x239,0x291,0x2e9,0x341,0x399,0x3f1,0x449,0x4a1,0x4f9,0x551,0x5a9,0x601,0x659,0x6b1,0x709,0x761,0x7b9 },
|
||||||
|
{ 0x25a,0x2b2,0x30a,0x362,0x3ba,0x412,0x46a,0x4c2,0x51a,0x572,0x5ca,0x622,0x67a,0x6d2,0x72a,0x782,0x7da,0x832,0x88a,0x026,0x07e,0x0d6,0x12e,0x186,0x1de,0x236,0x28e,0x2e6,0x33e,0x396,0x3ee,0x446,0x49e,0x4f6,0x54e,0x5a6,0x5fe,0x656,0x6ae,0x706,0x75e,0x7b6,0x80e },
|
||||||
|
{ 0x25b,0x2b3,0x30b,0x363,0x3bb,0x413,0x46b,0x4c3,0x51b,0x573,0x5cb,0x623,0x67b,0x6d3,0x72b,0x783,0x7db,0x833,0x88b,0x027,0x07f,0x0d7,0x12f,0x187,0x1df,0x237,0x28f,0x2e7,0x33f,0x397,0x3ef,0x447,0x49f,0x4f7,0x54f,0x5a7,0x5ff,0x657,0x6af,0x707,0x75f,0x7b7,0x80f },
|
||||||
|
{ 0x2b0,0x308,0x360,0x3b8,0x410,0x468,0x4c0,0x518,0x570,0x5c8,0x620,0x678,0x6d0,0x728,0x780,0x7d8,0x830,0x888,0x024,0x07c,0x0d4,0x12c,0x184,0x1dc,0x234,0x28c,0x2e4,0x33c,0x394,0x3ec,0x444,0x49c,0x4f4,0x54c,0x5a4,0x5fc,0x654,0x6ac,0x704,0x75c,0x7b4,0x80c,0x864 },
|
||||||
|
{ 0x2b1,0x309,0x361,0x3b9,0x411,0x469,0x4c1,0x519,0x571,0x5c9,0x621,0x679,0x6d1,0x729,0x781,0x7d9,0x831,0x889,0x025,0x07d,0x0d5,0x12d,0x185,0x1dd,0x235,0x28d,0x2e5,0x33d,0x395,0x3ed,0x445,0x49d,0x4f5,0x54d,0x5a5,0x5fd,0x655,0x6ad,0x705,0x75d,0x7b5,0x80d,0x865 },
|
||||||
|
{ 0x306,0x35e,0x3b6,0x40e,0x466,0x4be,0x516,0x56e,0x5c6,0x61e,0x676,0x6ce,0x726,0x77e,0x7d6,0x82e,0x886,0x022,0x07a,0x0d2,0x12a,0x182,0x1da,0x232,0x28a,0x2e2,0x33a,0x392,0x3ea,0x442,0x49a,0x4f2,0x54a,0x5a2,0x5fa,0x652,0x6aa,0x702,0x75a,0x7b2,0x80a,0x862,0x8ba },
|
||||||
|
{ 0x307,0x35f,0x3b7,0x40f,0x467,0x4bf,0x517,0x56f,0x5c7,0x61f,0x677,0x6cf,0x727,0x77f,0x7d7,0x82f,0x887,0x023,0x07b,0x0d3,0x12b,0x183,0x1db,0x233,0x28b,0x2e3,0x33b,0x393,0x3eb,0x443,0x49b,0x4f3,0x54b,0x5a3,0x5fb,0x653,0x6ab,0x703,0x75b,0x7b3,0x80b,0x863,0x8bb },
|
||||||
|
{ 0x35c,0x3b4,0x40c,0x464,0x4bc,0x514,0x56c,0x5c4,0x61c,0x674,0x6cc,0x724,0x77c,0x7d4,0x82c,0x884,0x020,0x078,0x0d0,0x128,0x180,0x1d8,0x230,0x288,0x2e0,0x338,0x390,0x3e8,0x440,0x498,0x4f0,0x548,0x5a0,0x5f8,0x650,0x6a8,0x700,0x758,0x7b0,0x808,0x860,0x8b8,0x054 },
|
||||||
|
{ 0x35d,0x3b5,0x40d,0x465,0x4bd,0x515,0x56d,0x5c5,0x61d,0x675,0x6cd,0x725,0x77d,0x7d5,0x82d,0x885,0x021,0x079,0x0d1,0x129,0x181,0x1d9,0x231,0x289,0x2e1,0x339,0x391,0x3e9,0x441,0x499,0x4f1,0x549,0x5a1,0x5f9,0x651,0x6a9,0x701,0x759,0x7b1,0x809,0x861,0x8b9,0x055 },
|
||||||
|
{ 0x3b2,0x40a,0x462,0x4ba,0x512,0x56a,0x5c2,0x61a,0x672,0x6ca,0x722,0x77a,0x7d2,0x82a,0x882,0x01e,0x076,0x0ce,0x126,0x17e,0x1d6,0x22e,0x286,0x2de,0x336,0x38e,0x3e6,0x43e,0x496,0x4ee,0x546,0x59e,0x5f6,0x64e,0x6a6,0x6fe,0x756,0x7ae,0x806,0x85e,0x8b6,0x052,0x0aa },
|
||||||
|
{ 0x3b3,0x40b,0x463,0x4bb,0x513,0x56b,0x5c3,0x61b,0x673,0x6cb,0x723,0x77b,0x7d3,0x82b,0x883,0x01f,0x077,0x0cf,0x127,0x17f,0x1d7,0x22f,0x287,0x2df,0x337,0x38f,0x3e7,0x43f,0x497,0x4ef,0x547,0x59f,0x5f7,0x64f,0x6a7,0x6ff,0x757,0x7af,0x807,0x85f,0x8b7,0x053,0x0ab },
|
||||||
|
{ 0x408,0x460,0x4b8,0x510,0x568,0x5c0,0x618,0x670,0x6c8,0x720,0x778,0x7d0,0x828,0x880,0x01c,0x074,0x0cc,0x124,0x17c,0x1d4,0x22c,0x284,0x2dc,0x334,0x38c,0x3e4,0x43c,0x494,0x4ec,0x544,0x59c,0x5f4,0x64c,0x6a4,0x6fc,0x754,0x7ac,0x804,0x85c,0x8b4,0x050,0x0a8,0x100 },
|
||||||
|
{ 0x409,0x461,0x4b9,0x511,0x569,0x5c1,0x619,0x671,0x6c9,0x721,0x779,0x7d1,0x829,0x881,0x01d,0x075,0x0cd,0x125,0x17d,0x1d5,0x22d,0x285,0x2dd,0x335,0x38d,0x3e5,0x43d,0x495,0x4ed,0x545,0x59d,0x5f5,0x64d,0x6a5,0x6fd,0x755,0x7ad,0x805,0x85d,0x8b5,0x051,0x0a9,0x101 },
|
||||||
|
{ 0x45e,0x4b6,0x50e,0x566,0x5be,0x616,0x66e,0x6c6,0x71e,0x776,0x7ce,0x826,0x87e,0x01a,0x072,0x0ca,0x122,0x17a,0x1d2,0x22a,0x282,0x2da,0x332,0x38a,0x3e2,0x43a,0x492,0x4ea,0x542,0x59a,0x5f2,0x64a,0x6a2,0x6fa,0x752,0x7aa,0x802,0x85a,0x8b2,0x04e,0x0a6,0x0fe,0x156 },
|
||||||
|
{ 0x45f,0x4b7,0x50f,0x567,0x5bf,0x617,0x66f,0x6c7,0x71f,0x777,0x7cf,0x827,0x87f,0x01b,0x073,0x0cb,0x123,0x17b,0x1d3,0x22b,0x283,0x2db,0x333,0x38b,0x3e3,0x43b,0x493,0x4eb,0x543,0x59b,0x5f3,0x64b,0x6a3,0x6fb,0x753,0x7ab,0x803,0x85b,0x8b3,0x04f,0x0a7,0x0ff,0x157 },
|
||||||
|
{ 0x4b4,0x50c,0x564,0x5bc,0x614,0x66c,0x6c4,0x71c,0x774,0x7cc,0x824,0x87c,0x018,0x070,0x0c8,0x120,0x178,0x1d0,0x228,0x280,0x2d8,0x330,0x388,0x3e0,0x438,0x490,0x4e8,0x540,0x598,0x5f0,0x648,0x6a0,0x6f8,0x750,0x7a8,0x800,0x858,0x8b0,0x04c,0x0a4,0x0fc,0x154,0x1ac },
|
||||||
|
{ 0x4b5,0x50d,0x565,0x5bd,0x615,0x66d,0x6c5,0x71d,0x775,0x7cd,0x825,0x87d,0x019,0x071,0x0c9,0x121,0x179,0x1d1,0x229,0x281,0x2d9,0x331,0x389,0x3e1,0x439,0x491,0x4e9,0x541,0x599,0x5f1,0x649,0x6a1,0x6f9,0x751,0x7a9,0x801,0x859,0x8b1,0x04d,0x0a5,0x0fd,0x155,0x1ad },
|
||||||
|
{ 0x50a,0x562,0x5ba,0x612,0x66a,0x6c2,0x71a,0x772,0x7ca,0x822,0x87a,0x016,0x06e,0x0c6,0x11e,0x176,0x1ce,0x226,0x27e,0x2d6,0x32e,0x386,0x3de,0x436,0x48e,0x4e6,0x53e,0x596,0x5ee,0x646,0x69e,0x6f6,0x74e,0x7a6,0x7fe,0x856,0x8ae,0x04a,0x0a2,0x0fa,0x152,0x1aa,0x202 },
|
||||||
|
{ 0x50b,0x563,0x5bb,0x613,0x66b,0x6c3,0x71b,0x773,0x7cb,0x823,0x87b,0x017,0x06f,0x0c7,0x11f,0x177,0x1cf,0x227,0x27f,0x2d7,0x32f,0x387,0x3df,0x437,0x48f,0x4e7,0x53f,0x597,0x5ef,0x647,0x69f,0x6f7,0x74f,0x7a7,0x7ff,0x857,0x8af,0x04b,0x0a3,0x0fb,0x153,0x1ab,0x203 },
|
||||||
|
{ 0x560,0x5b8,0x610,0x668,0x6c0,0x718,0x770,0x7c8,0x820,0x878,0x014,0x06c,0x0c4,0x11c,0x174,0x1cc,0x224,0x27c,0x2d4,0x32c,0x384,0x3dc,0x434,0x48c,0x4e4,0x53c,0x594,0x5ec,0x644,0x69c,0x6f4,0x74c,0x7a4,0x7fc,0x854,0x8ac,0x048,0x0a0,0x0f8,0x150,0x1a8,0x200,0x258 },
|
||||||
|
{ 0x561,0x5b9,0x611,0x669,0x6c1,0x719,0x771,0x7c9,0x821,0x879,0x015,0x06d,0x0c5,0x11d,0x175,0x1cd,0x225,0x27d,0x2d5,0x32d,0x385,0x3dd,0x435,0x48d,0x4e5,0x53d,0x595,0x5ed,0x645,0x69d,0x6f5,0x74d,0x7a5,0x7fd,0x855,0x8ad,0x049,0x0a1,0x0f9,0x151,0x1a9,0x201,0x259 },
|
||||||
|
{ 0x5b6,0x60e,0x666,0x6be,0x716,0x76e,0x7c6,0x81e,0x876,0x012,0x06a,0x0c2,0x11a,0x172,0x1ca,0x222,0x27a,0x2d2,0x32a,0x382,0x3da,0x432,0x48a,0x4e2,0x53a,0x592,0x5ea,0x642,0x69a,0x6f2,0x74a,0x7a2,0x7fa,0x852,0x8aa,0x046,0x09e,0x0f6,0x14e,0x1a6,0x1fe,0x256,0x2ae },
|
||||||
|
{ 0x5b7,0x60f,0x667,0x6bf,0x717,0x76f,0x7c7,0x81f,0x877,0x013,0x06b,0x0c3,0x11b,0x173,0x1cb,0x223,0x27b,0x2d3,0x32b,0x383,0x3db,0x433,0x48b,0x4e3,0x53b,0x593,0x5eb,0x643,0x69b,0x6f3,0x74b,0x7a3,0x7fb,0x853,0x8ab,0x047,0x09f,0x0f7,0x14f,0x1a7,0x1ff,0x257,0x2af },
|
||||||
|
{ 0x60c,0x664,0x6bc,0x714,0x76c,0x7c4,0x81c,0x874,0x010,0x068,0x0c0,0x118,0x170,0x1c8,0x220,0x278,0x2d0,0x328,0x380,0x3d8,0x430,0x488,0x4e0,0x538,0x590,0x5e8,0x640,0x698,0x6f0,0x748,0x7a0,0x7f8,0x850,0x8a8,0x044,0x09c,0x0f4,0x14c,0x1a4,0x1fc,0x254,0x2ac,0x304 },
|
||||||
|
{ 0x60d,0x665,0x6bd,0x715,0x76d,0x7c5,0x81d,0x875,0x011,0x069,0x0c1,0x119,0x171,0x1c9,0x221,0x279,0x2d1,0x329,0x381,0x3d9,0x431,0x489,0x4e1,0x539,0x591,0x5e9,0x641,0x699,0x6f1,0x749,0x7a1,0x7f9,0x851,0x8a9,0x045,0x09d,0x0f5,0x14d,0x1a5,0x1fd,0x255,0x2ad,0x305 },
|
||||||
|
{ 0x662,0x6ba,0x712,0x76a,0x7c2,0x81a,0x872,0x00e,0x066,0x0be,0x116,0x16e,0x1c6,0x21e,0x276,0x2ce,0x326,0x37e,0x3d6,0x42e,0x486,0x4de,0x536,0x58e,0x5e6,0x63e,0x696,0x6ee,0x746,0x79e,0x7f6,0x84e,0x8a6,0x042,0x09a,0x0f2,0x14a,0x1a2,0x1fa,0x252,0x2aa,0x302,0x35a },
|
||||||
|
{ 0x663,0x6bb,0x713,0x76b,0x7c3,0x81b,0x873,0x00f,0x067,0x0bf,0x117,0x16f,0x1c7,0x21f,0x277,0x2cf,0x327,0x37f,0x3d7,0x42f,0x487,0x4df,0x537,0x58f,0x5e7,0x63f,0x697,0x6ef,0x747,0x79f,0x7f7,0x84f,0x8a7,0x043,0x09b,0x0f3,0x14b,0x1a3,0x1fb,0x253,0x2ab,0x303,0x35b },
|
||||||
|
{ 0x6b8,0x710,0x768,0x7c0,0x818,0x870,0x00c,0x064,0x0bc,0x114,0x16c,0x1c4,0x21c,0x274,0x2cc,0x324,0x37c,0x3d4,0x42c,0x484,0x4dc,0x534,0x58c,0x5e4,0x63c,0x694,0x6ec,0x744,0x79c,0x7f4,0x84c,0x8a4,0x040,0x098,0x0f0,0x148,0x1a0,0x1f8,0x250,0x2a8,0x300,0x358,0x3b0 },
|
||||||
|
{ 0x6b9,0x711,0x769,0x7c1,0x819,0x871,0x00d,0x065,0x0bd,0x115,0x16d,0x1c5,0x21d,0x275,0x2cd,0x325,0x37d,0x3d5,0x42d,0x485,0x4dd,0x535,0x58d,0x5e5,0x63d,0x695,0x6ed,0x745,0x79d,0x7f5,0x84d,0x8a5,0x041,0x099,0x0f1,0x149,0x1a1,0x1f9,0x251,0x2a9,0x301,0x359,0x3b1 },
|
||||||
|
{ 0x70e,0x766,0x7be,0x816,0x86e,0x00a,0x062,0x0ba,0x112,0x16a,0x1c2,0x21a,0x272,0x2ca,0x322,0x37a,0x3d2,0x42a,0x482,0x4da,0x532,0x58a,0x5e2,0x63a,0x692,0x6ea,0x742,0x79a,0x7f2,0x84a,0x8a2,0x03e,0x096,0x0ee,0x146,0x19e,0x1f6,0x24e,0x2a6,0x2fe,0x356,0x3ae,0x406 },
|
||||||
|
{ 0x70f,0x767,0x7bf,0x817,0x86f,0x00b,0x063,0x0bb,0x113,0x16b,0x1c3,0x21b,0x273,0x2cb,0x323,0x37b,0x3d3,0x42b,0x483,0x4db,0x533,0x58b,0x5e3,0x63b,0x693,0x6eb,0x743,0x79b,0x7f3,0x84b,0x8a3,0x03f,0x097,0x0ef,0x147,0x19f,0x1f7,0x24f,0x2a7,0x2ff,0x357,0x3af,0x407 },
|
||||||
|
{ 0x764,0x7bc,0x814,0x86c,0x008,0x060,0x0b8,0x110,0x168,0x1c0,0x218,0x270,0x2c8,0x320,0x378,0x3d0,0x428,0x480,0x4d8,0x530,0x588,0x5e0,0x638,0x690,0x6e8,0x740,0x798,0x7f0,0x848,0x8a0,0x03c,0x094,0x0ec,0x144,0x19c,0x1f4,0x24c,0x2a4,0x2fc,0x354,0x3ac,0x404,0x45c },
|
||||||
|
{ 0x765,0x7bd,0x815,0x86d,0x009,0x061,0x0b9,0x111,0x169,0x1c1,0x219,0x271,0x2c9,0x321,0x379,0x3d1,0x429,0x481,0x4d9,0x531,0x589,0x5e1,0x639,0x691,0x6e9,0x741,0x799,0x7f1,0x849,0x8a1,0x03d,0x095,0x0ed,0x145,0x19d,0x1f5,0x24d,0x2a5,0x2fd,0x355,0x3ad,0x405,0x45d },
|
||||||
|
{ 0x7ba,0x812,0x86a,0x006,0x05e,0x0b6,0x10e,0x166,0x1be,0x216,0x26e,0x2c6,0x31e,0x376,0x3ce,0x426,0x47e,0x4d6,0x52e,0x586,0x5de,0x636,0x68e,0x6e6,0x73e,0x796,0x7ee,0x846,0x89e,0x03a,0x092,0x0ea,0x142,0x19a,0x1f2,0x24a,0x2a2,0x2fa,0x352,0x3aa,0x402,0x45a,0x4b2 },
|
||||||
|
{ 0x7bb,0x813,0x86b,0x007,0x05f,0x0b7,0x10f,0x167,0x1bf,0x217,0x26f,0x2c7,0x31f,0x377,0x3cf,0x427,0x47f,0x4d7,0x52f,0x587,0x5df,0x637,0x68f,0x6e7,0x73f,0x797,0x7ef,0x847,0x89f,0x03b,0x093,0x0eb,0x143,0x19b,0x1f3,0x24b,0x2a3,0x2fb,0x353,0x3ab,0x403,0x45b,0x4b3 },
|
||||||
|
{ 0x810,0x868,0x004,0x05c,0x0b4,0x10c,0x164,0x1bc,0x214,0x26c,0x2c4,0x31c,0x374,0x3cc,0x424,0x47c,0x4d4,0x52c,0x584,0x5dc,0x634,0x68c,0x6e4,0x73c,0x794,0x7ec,0x844,0x89c,0x038,0x090,0x0e8,0x140,0x198,0x1f0,0x248,0x2a0,0x2f8,0x350,0x3a8,0x400,0x458,0x4b0,0x508 },
|
||||||
|
{ 0x811,0x869,0x005,0x05d,0x0b5,0x10d,0x165,0x1bd,0x215,0x26d,0x2c5,0x31d,0x375,0x3cd,0x425,0x47d,0x4d5,0x52d,0x585,0x5dd,0x635,0x68d,0x6e5,0x73d,0x795,0x7ed,0x845,0x89d,0x039,0x091,0x0e9,0x141,0x199,0x1f1,0x249,0x2a1,0x2f9,0x351,0x3a9,0x401,0x459,0x4b1,0x509 },
|
||||||
|
{ 0x866,0x002,0x05a,0x0b2,0x10a,0x162,0x1ba,0x212,0x26a,0x2c2,0x31a,0x372,0x3ca,0x422,0x47a,0x4d2,0x52a,0x582,0x5da,0x632,0x68a,0x6e2,0x73a,0x792,0x7ea,0x842,0x89a,0x036,0x08e,0x0e6,0x13e,0x196,0x1ee,0x246,0x29e,0x2f6,0x34e,0x3a6,0x3fe,0x456,0x4ae,0x506,0x55e },
|
||||||
|
{ 0x867,0x003,0x05b,0x0b3,0x10b,0x163,0x1bb,0x213,0x26b,0x2c3,0x31b,0x373,0x3cb,0x423,0x47b,0x4d3,0x52b,0x583,0x5db,0x633,0x68b,0x6e3,0x73b,0x793,0x7eb,0x843,0x89b,0x037,0x08f,0x0e7,0x13f,0x197,0x1ef,0x247,0x29f,0x2f7,0x34f,0x3a7,0x3ff,0x457,0x4af,0x507,0x55f }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* ecc_source_byte - return data from the sector
|
||||||
|
* at the given offset, masking anything
|
||||||
|
* particular to a mode
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
static INLINE uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
|
||||||
|
{
|
||||||
|
/* in mode 2 always treat these as 0 bytes */
|
||||||
|
return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fn void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
|
||||||
|
*
|
||||||
|
* @brief -------------------------------------------------
|
||||||
|
* ecc_compute_bytes - calculate an ECC value (P or Q)
|
||||||
|
* -------------------------------------------------.
|
||||||
|
*
|
||||||
|
* @param sector The sector.
|
||||||
|
* @param row The row.
|
||||||
|
* @param rowlen The rowlen.
|
||||||
|
* @param [in,out] val1 The first value.
|
||||||
|
* @param [in,out] val2 The second value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t *val1, uint8_t *val2)
|
||||||
|
{
|
||||||
|
int component;
|
||||||
|
*val1 = *val2 = 0;
|
||||||
|
for (component = 0; component < rowlen; component++)
|
||||||
|
{
|
||||||
|
*val1 ^= ecc_source_byte(sector, row[component]);
|
||||||
|
*val2 ^= ecc_source_byte(sector, row[component]);
|
||||||
|
*val1 = ecclow[*val1];
|
||||||
|
}
|
||||||
|
*val1 = ecchigh[ecclow[*val1] ^ *val2];
|
||||||
|
*val2 ^= *val1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fn int ecc_verify(const uint8_t *sector)
|
||||||
|
*
|
||||||
|
* @brief -------------------------------------------------
|
||||||
|
* ecc_verify - verify the P and Q ECC codes in a sector
|
||||||
|
* -------------------------------------------------.
|
||||||
|
*
|
||||||
|
* @param sector The sector.
|
||||||
|
*
|
||||||
|
* @return true if it succeeds, false if it fails.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ecc_verify(const uint8_t *sector)
|
||||||
|
{
|
||||||
|
int byte;
|
||||||
|
/* first verify P bytes */
|
||||||
|
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
|
||||||
|
{
|
||||||
|
uint8_t val1, val2;
|
||||||
|
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &val1, &val2);
|
||||||
|
if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* then verify Q bytes */
|
||||||
|
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
|
||||||
|
{
|
||||||
|
uint8_t val1, val2;
|
||||||
|
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &val1, &val2);
|
||||||
|
if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fn void ecc_generate(uint8_t *sector)
|
||||||
|
*
|
||||||
|
* @brief -------------------------------------------------
|
||||||
|
* ecc_generate - generate the P and Q ECC codes for a sector, overwriting any
|
||||||
|
* existing codes
|
||||||
|
* -------------------------------------------------.
|
||||||
|
*
|
||||||
|
* @param [in,out] sector If non-null, the sector.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ecc_generate(uint8_t *sector)
|
||||||
|
{
|
||||||
|
int byte;
|
||||||
|
/* first verify P bytes */
|
||||||
|
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
|
||||||
|
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, §or[ECC_P_OFFSET + byte], §or[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]);
|
||||||
|
|
||||||
|
/* then verify Q bytes */
|
||||||
|
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
|
||||||
|
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, §or[ECC_Q_OFFSET + byte], §or[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fn void ecc_clear(uint8_t *sector)
|
||||||
|
*
|
||||||
|
* @brief -------------------------------------------------
|
||||||
|
* ecc_clear - erase the ECC P and Q cods to 0 within a sector
|
||||||
|
* -------------------------------------------------.
|
||||||
|
*
|
||||||
|
* @param [in,out] sector If non-null, the sector.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ecc_clear(uint8_t *sector)
|
||||||
|
{
|
||||||
|
memset(§or[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
|
||||||
|
memset(§or[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* WANT_RAW_DATA_SECTOR */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,322 @@
|
||||||
|
/* license:BSD-3-Clause
|
||||||
|
* copyright-holders:Aaron Giles
|
||||||
|
***************************************************************************
|
||||||
|
|
||||||
|
flac.c
|
||||||
|
|
||||||
|
FLAC compression wrappers
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libchdr/flac.h>
|
||||||
|
#include <libchdr/minmax.h>
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* FLAC DECODER
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
static FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
|
||||||
|
FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes);
|
||||||
|
static void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
|
||||||
|
static FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
|
||||||
|
static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
|
||||||
|
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void* client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
|
||||||
|
static void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* flac_decoder - constructor
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void flac_decoder_init(flac_decoder *decoder)
|
||||||
|
{
|
||||||
|
decoder->decoder = FLAC__stream_decoder_new();
|
||||||
|
decoder->sample_rate = 0;
|
||||||
|
decoder->channels = 0;
|
||||||
|
decoder->bits_per_sample = 0;
|
||||||
|
decoder->compressed_offset = 0;
|
||||||
|
decoder->compressed_start = NULL;
|
||||||
|
decoder->compressed_length = 0;
|
||||||
|
decoder->compressed2_start = NULL;
|
||||||
|
decoder->compressed2_length = 0;
|
||||||
|
decoder->uncompressed_offset = 0;
|
||||||
|
decoder->uncompressed_length = 0;
|
||||||
|
decoder->uncompressed_swap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* flac_decoder - destructor
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void flac_decoder_free(flac_decoder* decoder)
|
||||||
|
{
|
||||||
|
if ((decoder != NULL) && (decoder->decoder != NULL))
|
||||||
|
FLAC__stream_decoder_delete(decoder->decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* reset - reset state with the original
|
||||||
|
* parameters
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int flac_decoder_internal_reset(flac_decoder* decoder)
|
||||||
|
{
|
||||||
|
decoder->compressed_offset = 0;
|
||||||
|
if (FLAC__stream_decoder_init_stream(decoder->decoder,
|
||||||
|
&flac_decoder_read_callback_static,
|
||||||
|
NULL,
|
||||||
|
&flac_decoder_tell_callback_static,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&flac_decoder_write_callback_static,
|
||||||
|
&flac_decoder_metadata_callback_static,
|
||||||
|
&flac_decoder_error_callback_static, decoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||||
|
return 0;
|
||||||
|
return FLAC__stream_decoder_process_until_end_of_metadata(decoder->decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* reset - reset state with new memory parameters
|
||||||
|
* and a custom-generated header
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length)
|
||||||
|
{
|
||||||
|
/* modify the template header with our parameters */
|
||||||
|
static const uint8_t s_header_template[0x2a] =
|
||||||
|
{
|
||||||
|
0x66, 0x4C, 0x61, 0x43, /* +00: 'fLaC' stream header */
|
||||||
|
0x80, /* +04: metadata block type 0 (STREAMINFO), */
|
||||||
|
/* flagged as last block */
|
||||||
|
0x00, 0x00, 0x22, /* +05: metadata block length = 0x22 */
|
||||||
|
0x00, 0x00, /* +08: minimum block size */
|
||||||
|
0x00, 0x00, /* +0A: maximum block size */
|
||||||
|
0x00, 0x00, 0x00, /* +0C: minimum frame size (0 == unknown) */
|
||||||
|
0x00, 0x00, 0x00, /* +0F: maximum frame size (0 == unknown) */
|
||||||
|
0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, /* +12: sample rate (0x0ac44 == 44100), */
|
||||||
|
/* numchannels (2), sample bits (16), */
|
||||||
|
/* samples in stream (0 == unknown) */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* +1A: MD5 signature (0 == none) */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* +2A: start of stream data */
|
||||||
|
};
|
||||||
|
memcpy(decoder->custom_header, s_header_template, sizeof(s_header_template));
|
||||||
|
decoder->custom_header[0x08] = decoder->custom_header[0x0a] = block_size >> 8;
|
||||||
|
decoder->custom_header[0x09] = decoder->custom_header[0x0b] = block_size & 0xff;
|
||||||
|
decoder->custom_header[0x12] = sample_rate >> 12;
|
||||||
|
decoder->custom_header[0x13] = sample_rate >> 4;
|
||||||
|
decoder->custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1);
|
||||||
|
|
||||||
|
/* configure the header ahead of the provided buffer */
|
||||||
|
decoder->compressed_start = (const FLAC__byte *)(decoder->custom_header);
|
||||||
|
decoder->compressed_length = sizeof(decoder->custom_header);
|
||||||
|
decoder->compressed2_start = (const FLAC__byte *)(buffer);
|
||||||
|
decoder->compressed2_length = length;
|
||||||
|
return flac_decoder_internal_reset(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* decode_interleaved - decode to an interleaved
|
||||||
|
* sound stream
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian)
|
||||||
|
{
|
||||||
|
/* configure the uncompressed buffer */
|
||||||
|
memset(decoder->uncompressed_start, 0, sizeof(decoder->uncompressed_start));
|
||||||
|
decoder->uncompressed_start[0] = samples;
|
||||||
|
decoder->uncompressed_offset = 0;
|
||||||
|
decoder->uncompressed_length = num_samples;
|
||||||
|
decoder->uncompressed_swap = swap_endian;
|
||||||
|
|
||||||
|
/* loop until we get everything we want */
|
||||||
|
while (decoder->uncompressed_offset < decoder->uncompressed_length)
|
||||||
|
if (!FLAC__stream_decoder_process_single(decoder->decoder))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* decode - decode to an multiple independent
|
||||||
|
* data streams
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_endian)
|
||||||
|
{
|
||||||
|
/* make sure we don't have too many channels */
|
||||||
|
int chans = channels();
|
||||||
|
if (chans > ARRAY_LENGTH(m_uncompressed_start))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* configure the uncompressed buffer */
|
||||||
|
memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start));
|
||||||
|
for (int curchan = 0; curchan < chans; curchan++)
|
||||||
|
m_uncompressed_start[curchan] = samples[curchan];
|
||||||
|
m_uncompressed_offset = 0;
|
||||||
|
m_uncompressed_length = num_samples;
|
||||||
|
m_uncompressed_swap = swap_endian;
|
||||||
|
|
||||||
|
/* loop until we get everything we want */
|
||||||
|
while (m_uncompressed_offset < m_uncompressed_length)
|
||||||
|
if (!FLAC__stream_decoder_process_single(m_decoder))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* finish - finish up the decode
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t flac_decoder_finish(flac_decoder* decoder)
|
||||||
|
{
|
||||||
|
/* get the final decoding position and move forward */
|
||||||
|
FLAC__uint64 position = 0;
|
||||||
|
FLAC__stream_decoder_get_decode_position(decoder->decoder, &position);
|
||||||
|
FLAC__stream_decoder_finish(decoder->decoder);
|
||||||
|
|
||||||
|
/* adjust position if we provided the header */
|
||||||
|
if (position == 0)
|
||||||
|
return 0;
|
||||||
|
if (decoder->compressed_start == (const FLAC__byte *)(decoder->custom_header))
|
||||||
|
position -= decoder->compressed_length;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* read_callback - handle reads from the input
|
||||||
|
* stream
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
|
||||||
|
{
|
||||||
|
return flac_decoder_read_callback(client_data, buffer, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes)
|
||||||
|
{
|
||||||
|
flac_decoder* decoder = (flac_decoder*)client_data;
|
||||||
|
|
||||||
|
uint32_t expected = *bytes;
|
||||||
|
|
||||||
|
/* copy from primary buffer first */
|
||||||
|
uint32_t outputpos = 0;
|
||||||
|
if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length)
|
||||||
|
{
|
||||||
|
uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed_length - decoder->compressed_offset);
|
||||||
|
memcpy(&buffer[outputpos], decoder->compressed_start + decoder->compressed_offset, bytes_to_copy);
|
||||||
|
outputpos += bytes_to_copy;
|
||||||
|
decoder->compressed_offset += bytes_to_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* once we're out of that, copy from the secondary buffer */
|
||||||
|
if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length + decoder->compressed2_length)
|
||||||
|
{
|
||||||
|
uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed2_length - (decoder->compressed_offset - decoder->compressed_length));
|
||||||
|
memcpy(&buffer[outputpos], decoder->compressed2_start + decoder->compressed_offset - decoder->compressed_length, bytes_to_copy);
|
||||||
|
outputpos += bytes_to_copy;
|
||||||
|
decoder->compressed_offset += bytes_to_copy;
|
||||||
|
}
|
||||||
|
*bytes = outputpos;
|
||||||
|
|
||||||
|
/* return based on whether we ran out of data */
|
||||||
|
return (*bytes < expected) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* metadata_callback - handle STREAMINFO metadata
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
|
||||||
|
{
|
||||||
|
flac_decoder *fldecoder;
|
||||||
|
/* ignore all but STREAMINFO metadata */
|
||||||
|
if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* parse out the data we care about */
|
||||||
|
fldecoder = (flac_decoder *)(client_data);
|
||||||
|
fldecoder->sample_rate = metadata->data.stream_info.sample_rate;
|
||||||
|
fldecoder->bits_per_sample = metadata->data.stream_info.bits_per_sample;
|
||||||
|
fldecoder->channels = metadata->data.stream_info.channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* tell_callback - handle requests to find out
|
||||||
|
* where in the input stream we are
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
|
||||||
|
{
|
||||||
|
*absolute_byte_offset = ((flac_decoder *)client_data)->compressed_offset;
|
||||||
|
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* write_callback - handle writes to the output
|
||||||
|
* stream
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
|
||||||
|
{
|
||||||
|
return flac_decoder_write_callback(client_data, frame, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void *client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
|
||||||
|
{
|
||||||
|
int sampnum, chan;
|
||||||
|
int shift, blocksize;
|
||||||
|
flac_decoder * decoder = (flac_decoder *)client_data;
|
||||||
|
|
||||||
|
assert(frame->header.channels == decoder->channels);
|
||||||
|
|
||||||
|
/* interleaved case */
|
||||||
|
shift = decoder->uncompressed_swap ? 8 : 0;
|
||||||
|
blocksize = frame->header.blocksize;
|
||||||
|
if (decoder->uncompressed_start[1] == NULL)
|
||||||
|
{
|
||||||
|
int16_t *dest = decoder->uncompressed_start[0] + decoder->uncompressed_offset * frame->header.channels;
|
||||||
|
for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++)
|
||||||
|
for (chan = 0; chan < frame->header.channels; chan++)
|
||||||
|
*dest++ = (int16_t)((((uint16_t)buffer[chan][sampnum]) << shift) | (((uint16_t)buffer[chan][sampnum]) >> shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* non-interleaved case */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++)
|
||||||
|
for (chan = 0; chan < frame->header.channels; chan++)
|
||||||
|
if (decoder->uncompressed_start[chan] != NULL)
|
||||||
|
decoder->uncompressed_start[chan][decoder->uncompressed_offset] = (int16_t) ( (((uint16_t)(buffer[chan][sampnum])) << shift) | ( ((uint16_t)(buffer[chan][sampnum])) >> shift) );
|
||||||
|
}
|
||||||
|
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fn void flac_decoder::error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||||
|
*
|
||||||
|
* @brief -------------------------------------------------
|
||||||
|
* error_callback - handle errors (ignore them)
|
||||||
|
* -------------------------------------------------.
|
||||||
|
*
|
||||||
|
* @param decoder The decoder.
|
||||||
|
* @param status The status.
|
||||||
|
* @param [in,out] client_data If non-null, information describing the client.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,560 @@
|
||||||
|
/* license:BSD-3-Clause
|
||||||
|
* copyright-holders:Aaron Giles
|
||||||
|
****************************************************************************
|
||||||
|
|
||||||
|
huffman.c
|
||||||
|
|
||||||
|
Static Huffman compression and decompression helpers.
|
||||||
|
|
||||||
|
****************************************************************************
|
||||||
|
|
||||||
|
Maximum codelength is officially (alphabetsize - 1). This would be 255 bits
|
||||||
|
(since we use 1 byte values). However, it is also dependent upon the number
|
||||||
|
of samples used, as follows:
|
||||||
|
|
||||||
|
2 bits -> 3..4 samples
|
||||||
|
3 bits -> 5..7 samples
|
||||||
|
4 bits -> 8..12 samples
|
||||||
|
5 bits -> 13..20 samples
|
||||||
|
6 bits -> 21..33 samples
|
||||||
|
7 bits -> 34..54 samples
|
||||||
|
8 bits -> 55..88 samples
|
||||||
|
9 bits -> 89..143 samples
|
||||||
|
10 bits -> 144..232 samples
|
||||||
|
11 bits -> 233..376 samples
|
||||||
|
12 bits -> 377..609 samples
|
||||||
|
13 bits -> 610..986 samples
|
||||||
|
14 bits -> 987..1596 samples
|
||||||
|
15 bits -> 1597..2583 samples
|
||||||
|
16 bits -> 2584..4180 samples -> note that a 4k data size guarantees codelength <= 16 bits
|
||||||
|
17 bits -> 4181..6764 samples
|
||||||
|
18 bits -> 6765..10945 samples
|
||||||
|
19 bits -> 10946..17710 samples
|
||||||
|
20 bits -> 17711..28656 samples
|
||||||
|
21 bits -> 28657..46367 samples
|
||||||
|
22 bits -> 46368..75024 samples
|
||||||
|
23 bits -> 75025..121392 samples
|
||||||
|
24 bits -> 121393..196417 samples
|
||||||
|
25 bits -> 196418..317810 samples
|
||||||
|
26 bits -> 317811..514228 samples
|
||||||
|
27 bits -> 514229..832039 samples
|
||||||
|
28 bits -> 832040..1346268 samples
|
||||||
|
29 bits -> 1346269..2178308 samples
|
||||||
|
30 bits -> 2178309..3524577 samples
|
||||||
|
31 bits -> 3524578..5702886 samples
|
||||||
|
32 bits -> 5702887..9227464 samples
|
||||||
|
|
||||||
|
Looking at it differently, here is where powers of 2 fall into these buckets:
|
||||||
|
|
||||||
|
256 samples -> 11 bits max
|
||||||
|
512 samples -> 12 bits max
|
||||||
|
1k samples -> 14 bits max
|
||||||
|
2k samples -> 15 bits max
|
||||||
|
4k samples -> 16 bits max
|
||||||
|
8k samples -> 18 bits max
|
||||||
|
16k samples -> 19 bits max
|
||||||
|
32k samples -> 21 bits max
|
||||||
|
64k samples -> 22 bits max
|
||||||
|
128k samples -> 24 bits max
|
||||||
|
256k samples -> 25 bits max
|
||||||
|
512k samples -> 27 bits max
|
||||||
|
1M samples -> 28 bits max
|
||||||
|
2M samples -> 29 bits max
|
||||||
|
4M samples -> 31 bits max
|
||||||
|
8M samples -> 32 bits max
|
||||||
|
|
||||||
|
****************************************************************************
|
||||||
|
|
||||||
|
Delta-RLE encoding works as follows:
|
||||||
|
|
||||||
|
Starting value is assumed to be 0. All data is encoded as a delta
|
||||||
|
from the previous value, such that final[i] = final[i - 1] + delta.
|
||||||
|
Long runs of 0s are RLE-encoded as follows:
|
||||||
|
|
||||||
|
0x100 = repeat count of 8
|
||||||
|
0x101 = repeat count of 9
|
||||||
|
0x102 = repeat count of 10
|
||||||
|
0x103 = repeat count of 11
|
||||||
|
0x104 = repeat count of 12
|
||||||
|
0x105 = repeat count of 13
|
||||||
|
0x106 = repeat count of 14
|
||||||
|
0x107 = repeat count of 15
|
||||||
|
0x108 = repeat count of 16
|
||||||
|
0x109 = repeat count of 32
|
||||||
|
0x10a = repeat count of 64
|
||||||
|
0x10b = repeat count of 128
|
||||||
|
0x10c = repeat count of 256
|
||||||
|
0x10d = repeat count of 512
|
||||||
|
0x10e = repeat count of 1024
|
||||||
|
0x10f = repeat count of 2048
|
||||||
|
|
||||||
|
Note that repeat counts are reset at the end of a row, so if a 0 run
|
||||||
|
extends to the end of a row, a large repeat count may be used.
|
||||||
|
|
||||||
|
The reason for starting the run counts at 8 is that 0 is expected to
|
||||||
|
be the most common symbol, and is typically encoded in 1 or 2 bits.
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <libchdr/huffman.h>
|
||||||
|
#include <libchdr/minmax.h>
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* MACROS
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f))
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* IMPLEMENTATION
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* huffman_context_base - create an encoding/
|
||||||
|
* decoding context
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits)
|
||||||
|
{
|
||||||
|
struct huffman_decoder* decoder;
|
||||||
|
/* limit to 24 bits */
|
||||||
|
if (maxbits > 24)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
decoder = (struct huffman_decoder*)malloc(sizeof(struct huffman_decoder));
|
||||||
|
decoder->numcodes = numcodes;
|
||||||
|
decoder->maxbits = maxbits;
|
||||||
|
decoder->lookup = (lookup_value*)malloc(sizeof(lookup_value) * (1 << maxbits));
|
||||||
|
decoder->huffnode = (struct node_t*)malloc(sizeof(struct node_t) * numcodes);
|
||||||
|
decoder->datahisto = NULL;
|
||||||
|
decoder->prevdata = 0;
|
||||||
|
decoder->rleremaining = 0;
|
||||||
|
return decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_huffman_decoder(struct huffman_decoder* decoder)
|
||||||
|
{
|
||||||
|
if (decoder != NULL)
|
||||||
|
{
|
||||||
|
if (decoder->lookup != NULL)
|
||||||
|
free(decoder->lookup);
|
||||||
|
if (decoder->huffnode != NULL)
|
||||||
|
free(decoder->huffnode);
|
||||||
|
free(decoder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* decode_one - decode a single code from the
|
||||||
|
* huffman stream
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf)
|
||||||
|
{
|
||||||
|
/* peek ahead to get maxbits worth of data */
|
||||||
|
uint32_t bits = bitstream_peek(bitbuf, decoder->maxbits);
|
||||||
|
|
||||||
|
/* look it up, then remove the actual number of bits for this code */
|
||||||
|
lookup_value lookup = decoder->lookup[bits];
|
||||||
|
bitstream_remove(bitbuf, lookup & 0x1f);
|
||||||
|
|
||||||
|
/* return the value */
|
||||||
|
return lookup >> 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* import_tree_rle - import an RLE-encoded
|
||||||
|
* huffman tree from a source data stream
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf)
|
||||||
|
{
|
||||||
|
enum huffman_error error;
|
||||||
|
/* bits per entry depends on the maxbits */
|
||||||
|
int numbits;
|
||||||
|
int curnode;
|
||||||
|
if (decoder->maxbits >= 16)
|
||||||
|
numbits = 5;
|
||||||
|
else if (decoder->maxbits >= 8)
|
||||||
|
numbits = 4;
|
||||||
|
else
|
||||||
|
numbits = 3;
|
||||||
|
|
||||||
|
/* loop until we read all the nodes */
|
||||||
|
for (curnode = 0; curnode < decoder->numcodes; )
|
||||||
|
{
|
||||||
|
/* a non-one value is just raw */
|
||||||
|
int nodebits = bitstream_read(bitbuf, numbits);
|
||||||
|
if (nodebits != 1)
|
||||||
|
decoder->huffnode[curnode++].numbits = nodebits;
|
||||||
|
|
||||||
|
/* a one value is an escape code */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* a double 1 is just a single 1 */
|
||||||
|
nodebits = bitstream_read(bitbuf, numbits);
|
||||||
|
if (nodebits == 1)
|
||||||
|
decoder->huffnode[curnode++].numbits = nodebits;
|
||||||
|
|
||||||
|
/* otherwise, we need one for value for the repeat count */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int repcount = bitstream_read(bitbuf, numbits) + 3;
|
||||||
|
while (repcount--)
|
||||||
|
decoder->huffnode[curnode++].numbits = nodebits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure we ended up with the right number */
|
||||||
|
if (curnode != decoder->numcodes)
|
||||||
|
return HUFFERR_INVALID_DATA;
|
||||||
|
|
||||||
|
/* assign canonical codes for all nodes based on their code lengths */
|
||||||
|
error = huffman_assign_canonical_codes(decoder);
|
||||||
|
if (error != HUFFERR_NONE)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* build the lookup table */
|
||||||
|
huffman_build_lookup_table(decoder);
|
||||||
|
|
||||||
|
/* determine final input length and report errors */
|
||||||
|
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* import_tree_huffman - import a huffman-encoded
|
||||||
|
* huffman tree from a source data stream
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf)
|
||||||
|
{
|
||||||
|
int last = 0;
|
||||||
|
int curcode;
|
||||||
|
uint32_t temp;
|
||||||
|
enum huffman_error error;
|
||||||
|
uint8_t rlefullbits = 0;
|
||||||
|
int index, count = 0;
|
||||||
|
int start;
|
||||||
|
/* start by parsing the lengths for the small tree */
|
||||||
|
struct huffman_decoder* smallhuff = create_huffman_decoder(24, 6);
|
||||||
|
|
||||||
|
smallhuff->huffnode[0].numbits = bitstream_read(bitbuf, 3);
|
||||||
|
start = bitstream_read(bitbuf, 3) + 1;
|
||||||
|
|
||||||
|
for (index = 1; index < 24; index++)
|
||||||
|
{
|
||||||
|
if (index < start || count == 7)
|
||||||
|
smallhuff->huffnode[index].numbits = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count = bitstream_read(bitbuf, 3);
|
||||||
|
smallhuff->huffnode[index].numbits = (count == 7) ? 0 : count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* then regenerate the tree */
|
||||||
|
error = huffman_assign_canonical_codes(smallhuff);
|
||||||
|
if (error != HUFFERR_NONE)
|
||||||
|
return error;
|
||||||
|
huffman_build_lookup_table(smallhuff);
|
||||||
|
|
||||||
|
/* determine the maximum length of an RLE count */
|
||||||
|
temp = decoder->numcodes - 9;
|
||||||
|
while (temp != 0)
|
||||||
|
temp >>= 1, rlefullbits++;
|
||||||
|
|
||||||
|
/* now process the rest of the data */
|
||||||
|
for (curcode = 0; curcode < decoder->numcodes; )
|
||||||
|
{
|
||||||
|
int value = huffman_decode_one(smallhuff, bitbuf);
|
||||||
|
if (value != 0)
|
||||||
|
decoder->huffnode[curcode++].numbits = last = value - 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int count = bitstream_read(bitbuf, 3) + 2;
|
||||||
|
if (count == 7+2)
|
||||||
|
count += bitstream_read(bitbuf, rlefullbits);
|
||||||
|
for ( ; count != 0 && curcode < decoder->numcodes; count--)
|
||||||
|
decoder->huffnode[curcode++].numbits = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure we ended up with the right number */
|
||||||
|
if (curcode != decoder->numcodes)
|
||||||
|
return HUFFERR_INVALID_DATA;
|
||||||
|
|
||||||
|
/* assign canonical codes for all nodes based on their code lengths */
|
||||||
|
error = huffman_assign_canonical_codes(decoder);
|
||||||
|
if (error != HUFFERR_NONE)
|
||||||
|
{
|
||||||
|
delete_huffman_decoder(smallhuff);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the lookup table */
|
||||||
|
huffman_build_lookup_table(decoder);
|
||||||
|
delete_huffman_decoder(smallhuff);
|
||||||
|
|
||||||
|
/* determine final input length and report errors */
|
||||||
|
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* compute_tree_from_histo - common backend for
|
||||||
|
* computing a tree based on the data histogram
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder)
|
||||||
|
{
|
||||||
|
/* compute the number of data items in the histogram */
|
||||||
|
int i;
|
||||||
|
uint32_t upperweight;
|
||||||
|
uint32_t lowerweight = 0;
|
||||||
|
uint32_t sdatacount = 0;
|
||||||
|
for (i = 0; i < decoder->numcodes; i++)
|
||||||
|
sdatacount += decoder->datahisto[i];
|
||||||
|
|
||||||
|
/* binary search to achieve the optimum encoding */
|
||||||
|
upperweight = sdatacount * 2;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
/* build a tree using the current weight */
|
||||||
|
uint32_t curweight = (upperweight + lowerweight) / 2;
|
||||||
|
int curmaxbits = huffman_build_tree(decoder, sdatacount, curweight);
|
||||||
|
|
||||||
|
/* apply binary search here */
|
||||||
|
if (curmaxbits <= decoder->maxbits)
|
||||||
|
{
|
||||||
|
lowerweight = curweight;
|
||||||
|
|
||||||
|
/* early out if it worked with the raw weights, or if we're done searching */
|
||||||
|
if (curweight == sdatacount || (upperweight - lowerweight) <= 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
upperweight = curweight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assign canonical codes for all nodes based on their code lengths */
|
||||||
|
return huffman_assign_canonical_codes(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* INTERNAL FUNCTIONS
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* tree_node_compare - compare two tree nodes
|
||||||
|
* by weight
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int huffman_tree_node_compare(const void *item1, const void *item2)
|
||||||
|
{
|
||||||
|
const struct node_t *node1 = *(const struct node_t **)item1;
|
||||||
|
const struct node_t *node2 = *(const struct node_t **)item2;
|
||||||
|
if (node2->weight != node1->weight)
|
||||||
|
return node2->weight - node1->weight;
|
||||||
|
if (node2->bits - node1->bits == 0)
|
||||||
|
fprintf(stderr, "identical node sort keys, should not happen!\n");
|
||||||
|
return (int)node1->bits - (int)node2->bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* build_tree - build a huffman tree based on the
|
||||||
|
* data distribution
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight)
|
||||||
|
{
|
||||||
|
int nextalloc;
|
||||||
|
int maxbits = 0;
|
||||||
|
/* make a list of all non-zero nodes */
|
||||||
|
struct node_t** list = (struct node_t**)
|
||||||
|
malloc(sizeof(struct node_t*) * decoder->numcodes * 2);
|
||||||
|
int curcode, listitems = 0;
|
||||||
|
|
||||||
|
memset(decoder->huffnode, 0,
|
||||||
|
decoder->numcodes * sizeof(decoder->huffnode[0]));
|
||||||
|
|
||||||
|
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||||
|
if (decoder->datahisto[curcode] != 0)
|
||||||
|
{
|
||||||
|
list[listitems++] = &decoder->huffnode[curcode];
|
||||||
|
decoder->huffnode[curcode].count = decoder->datahisto[curcode];
|
||||||
|
decoder->huffnode[curcode].bits = curcode;
|
||||||
|
|
||||||
|
/* scale the weight by the current effective length, ensuring we don't go to 0 */
|
||||||
|
decoder->huffnode[curcode].weight = ((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata);
|
||||||
|
if (decoder->huffnode[curcode].weight == 0)
|
||||||
|
decoder->huffnode[curcode].weight = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
fprintf(stderr, "Pre-sort:\n");
|
||||||
|
for (i = 0; i < listitems; i++)
|
||||||
|
fprintf(stderr, "weight: %d code: %d\n",
|
||||||
|
list[i]->m_weight, list[i]->m_bits);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* sort the list by weight, largest weight first */
|
||||||
|
qsort(&list[0], listitems, sizeof(list[0]), huffman_tree_node_compare);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
fprintf(stderr, "Post-sort:\n");
|
||||||
|
for (int i = 0; i < listitems; i++) {
|
||||||
|
fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "===================\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* now build the tree */
|
||||||
|
nextalloc = decoder->numcodes;
|
||||||
|
|
||||||
|
while (listitems > 1)
|
||||||
|
{
|
||||||
|
int curitem;
|
||||||
|
/* remove lowest two items */
|
||||||
|
struct node_t* node1 = &(*list[--listitems]);
|
||||||
|
struct node_t* node0 = &(*list[--listitems]);
|
||||||
|
|
||||||
|
/* create new node */
|
||||||
|
struct node_t* newnode = &decoder->huffnode[nextalloc++];
|
||||||
|
newnode->parent = NULL;
|
||||||
|
node0->parent =
|
||||||
|
node1->parent = newnode;
|
||||||
|
newnode->weight =
|
||||||
|
node0->weight + node1->weight;
|
||||||
|
|
||||||
|
/* insert into list at appropriate location */
|
||||||
|
for (curitem = 0; curitem < listitems; curitem++)
|
||||||
|
if (newnode->weight > list[curitem]->weight)
|
||||||
|
{
|
||||||
|
memmove(&list[curitem+1],
|
||||||
|
&list[curitem],
|
||||||
|
(listitems - curitem) * sizeof(list[0]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
list[curitem] = newnode;
|
||||||
|
listitems++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute the number of bits in each code,
|
||||||
|
* and fill in another histogram */
|
||||||
|
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||||
|
{
|
||||||
|
struct node_t *curnode;
|
||||||
|
struct node_t* node = &decoder->huffnode[curcode];
|
||||||
|
node->numbits = 0;
|
||||||
|
node->bits = 0;
|
||||||
|
|
||||||
|
/* if we have a non-zero weight, compute the number of bits */
|
||||||
|
if (node->weight > 0)
|
||||||
|
{
|
||||||
|
/* determine the number of bits for this node */
|
||||||
|
for (curnode = node;
|
||||||
|
curnode->parent != NULL; curnode = curnode->parent)
|
||||||
|
node->numbits++;
|
||||||
|
if (node->numbits == 0)
|
||||||
|
node->numbits = 1;
|
||||||
|
|
||||||
|
/* keep track of the max */
|
||||||
|
maxbits = MAX(maxbits, ((int)node->numbits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(list);
|
||||||
|
return maxbits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* assign_canonical_codes - assign canonical codes
|
||||||
|
* to all the nodes based on the number of bits
|
||||||
|
* in each
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder)
|
||||||
|
{
|
||||||
|
uint32_t curstart = 0;
|
||||||
|
/* build up a histogram of bit lengths */
|
||||||
|
int curcode, codelen;
|
||||||
|
uint32_t bithisto[33] = { 0 };
|
||||||
|
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||||
|
{
|
||||||
|
struct node_t* node = &decoder->huffnode[curcode];
|
||||||
|
if (node->numbits > decoder->maxbits)
|
||||||
|
return HUFFERR_INTERNAL_INCONSISTENCY;
|
||||||
|
if (node->numbits <= 32)
|
||||||
|
bithisto[node->numbits]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for each code length, determine the starting code number */
|
||||||
|
for (codelen = 32; codelen > 0; codelen--)
|
||||||
|
{
|
||||||
|
uint32_t nextstart = (curstart + bithisto[codelen]) >> 1;
|
||||||
|
if (codelen != 1 && nextstart * 2 != (curstart + bithisto[codelen]))
|
||||||
|
return HUFFERR_INTERNAL_INCONSISTENCY;
|
||||||
|
bithisto[codelen] = curstart;
|
||||||
|
curstart = nextstart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now assign canonical codes */
|
||||||
|
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||||
|
{
|
||||||
|
struct node_t* node = &decoder->huffnode[curcode];
|
||||||
|
if (node->numbits > 0)
|
||||||
|
node->bits = bithisto[node->numbits]++;
|
||||||
|
}
|
||||||
|
return HUFFERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
* build_lookup_table - build a lookup table for
|
||||||
|
* fast decoding
|
||||||
|
*-------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void huffman_build_lookup_table(struct huffman_decoder* decoder)
|
||||||
|
{
|
||||||
|
/* iterate over all codes */
|
||||||
|
int curcode;
|
||||||
|
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||||
|
{
|
||||||
|
/* process all nodes which have non-zero bits */
|
||||||
|
struct node_t* node = &decoder->huffnode[curcode];
|
||||||
|
if (node->numbits > 0)
|
||||||
|
{
|
||||||
|
int shift;
|
||||||
|
lookup_value *dest;
|
||||||
|
lookup_value *destend;
|
||||||
|
/* set up the entry */
|
||||||
|
lookup_value value = MAKE_LOOKUP(curcode, node->numbits);
|
||||||
|
|
||||||
|
/* fill all matching entries */
|
||||||
|
shift = decoder->maxbits - node->numbits;
|
||||||
|
dest = &decoder->lookup[node->bits << shift];
|
||||||
|
destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
|
||||||
|
while (dest <= destend)
|
||||||
|
*dest++ = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,391 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rpng_encode.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <encodings/crc32.h>
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <streams/trans_stream.h>
|
||||||
|
|
||||||
|
#include "rpng_internal.h"
|
||||||
|
|
||||||
|
#undef GOTO_END_ERROR
|
||||||
|
#define GOTO_END_ERROR() do { \
|
||||||
|
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); \
|
||||||
|
ret = false; \
|
||||||
|
goto end; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static void dword_write_be(uint8_t *buf, uint32_t val)
|
||||||
|
{
|
||||||
|
*buf++ = (uint8_t)(val >> 24);
|
||||||
|
*buf++ = (uint8_t)(val >> 16);
|
||||||
|
*buf++ = (uint8_t)(val >> 8);
|
||||||
|
*buf++ = (uint8_t)(val >> 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool png_write_crc(RFILE *file, const uint8_t *data, size_t size)
|
||||||
|
{
|
||||||
|
uint8_t crc_raw[4] = {0};
|
||||||
|
uint32_t crc = encoding_crc32(0, data, size);
|
||||||
|
|
||||||
|
dword_write_be(crc_raw, crc);
|
||||||
|
return filestream_write(file, crc_raw, sizeof(crc_raw)) == sizeof(crc_raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool png_write_ihdr(RFILE *file, const struct png_ihdr *ihdr)
|
||||||
|
{
|
||||||
|
uint8_t ihdr_raw[21];
|
||||||
|
|
||||||
|
ihdr_raw[0] = '0'; /* Size */
|
||||||
|
ihdr_raw[1] = '0';
|
||||||
|
ihdr_raw[2] = '0';
|
||||||
|
ihdr_raw[3] = '0';
|
||||||
|
ihdr_raw[4] = 'I';
|
||||||
|
ihdr_raw[5] = 'H';
|
||||||
|
ihdr_raw[6] = 'D';
|
||||||
|
ihdr_raw[7] = 'R';
|
||||||
|
ihdr_raw[8] = 0; /* Width */
|
||||||
|
ihdr_raw[9] = 0;
|
||||||
|
ihdr_raw[10] = 0;
|
||||||
|
ihdr_raw[11] = 0;
|
||||||
|
ihdr_raw[12] = 0; /* Height */
|
||||||
|
ihdr_raw[13] = 0;
|
||||||
|
ihdr_raw[14] = 0;
|
||||||
|
ihdr_raw[15] = 0;
|
||||||
|
ihdr_raw[16] = ihdr->depth; /* Depth */
|
||||||
|
ihdr_raw[17] = ihdr->color_type;
|
||||||
|
ihdr_raw[18] = ihdr->compression;
|
||||||
|
ihdr_raw[19] = ihdr->filter;
|
||||||
|
ihdr_raw[20] = ihdr->interlace;
|
||||||
|
|
||||||
|
dword_write_be(ihdr_raw + 0, sizeof(ihdr_raw) - 8);
|
||||||
|
dword_write_be(ihdr_raw + 8, ihdr->width);
|
||||||
|
dword_write_be(ihdr_raw + 12, ihdr->height);
|
||||||
|
if (filestream_write(file, ihdr_raw, sizeof(ihdr_raw)) != sizeof(ihdr_raw))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!png_write_crc(file, ihdr_raw + sizeof(uint32_t),
|
||||||
|
sizeof(ihdr_raw) - sizeof(uint32_t)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool png_write_idat(RFILE *file, const uint8_t *data, size_t size)
|
||||||
|
{
|
||||||
|
if (filestream_write(file, data, size) != (ssize_t)size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!png_write_crc(file, data + sizeof(uint32_t), size - sizeof(uint32_t)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool png_write_iend(RFILE *file)
|
||||||
|
{
|
||||||
|
const uint8_t data[] = {
|
||||||
|
0, 0, 0, 0,
|
||||||
|
'I', 'E', 'N', 'D',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filestream_write(file, data, sizeof(data)) != sizeof(data))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!png_write_crc(file, data + sizeof(uint32_t),
|
||||||
|
sizeof(data) - sizeof(uint32_t)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_argb_line(uint8_t *dst, const uint32_t *src, unsigned width)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
uint32_t col = src[i];
|
||||||
|
*dst++ = (uint8_t)(col >> 16);
|
||||||
|
*dst++ = (uint8_t)(col >> 8);
|
||||||
|
*dst++ = (uint8_t)(col >> 0);
|
||||||
|
*dst++ = (uint8_t)(col >> 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_bgr24_line(uint8_t *dst, const uint8_t *src, unsigned width)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < width; i++, dst += 3, src += 3)
|
||||||
|
{
|
||||||
|
dst[2] = src[0];
|
||||||
|
dst[1] = src[1];
|
||||||
|
dst[0] = src[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned count_sad(const uint8_t *data, size_t size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
unsigned cnt = 0;
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
cnt += abs((int8_t)data[i]);
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned filter_up(uint8_t *target, const uint8_t *line,
|
||||||
|
const uint8_t *prev, unsigned width, unsigned bpp)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
width *= bpp;
|
||||||
|
for (i = 0; i < width; i++)
|
||||||
|
target[i] = line[i] - prev[i];
|
||||||
|
|
||||||
|
return count_sad(target, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned filter_sub(uint8_t *target, const uint8_t *line,
|
||||||
|
unsigned width, unsigned bpp)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
width *= bpp;
|
||||||
|
for (i = 0; i < bpp; i++)
|
||||||
|
target[i] = line[i];
|
||||||
|
for (i = bpp; i < width; i++)
|
||||||
|
target[i] = line[i] - line[i - bpp];
|
||||||
|
|
||||||
|
return count_sad(target, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned filter_avg(uint8_t *target, const uint8_t *line,
|
||||||
|
const uint8_t *prev, unsigned width, unsigned bpp)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
width *= bpp;
|
||||||
|
for (i = 0; i < bpp; i++)
|
||||||
|
target[i] = line[i] - (prev[i] >> 1);
|
||||||
|
for (i = bpp; i < width; i++)
|
||||||
|
target[i] = line[i] - ((line[i - bpp] + prev[i]) >> 1);
|
||||||
|
|
||||||
|
return count_sad(target, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned filter_paeth(uint8_t *target,
|
||||||
|
const uint8_t *line, const uint8_t *prev,
|
||||||
|
unsigned width, unsigned bpp)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
width *= bpp;
|
||||||
|
for (i = 0; i < bpp; i++)
|
||||||
|
target[i] = line[i] - paeth(0, prev[i], 0);
|
||||||
|
for (i = bpp; i < width; i++)
|
||||||
|
target[i] = line[i] - paeth(line[i - bpp], prev[i], prev[i - bpp]);
|
||||||
|
|
||||||
|
return count_sad(target, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rpng_save_image(const char *path,
|
||||||
|
const uint8_t *data,
|
||||||
|
unsigned width, unsigned height, unsigned pitch, unsigned bpp)
|
||||||
|
{
|
||||||
|
unsigned h;
|
||||||
|
bool ret = true;
|
||||||
|
struct png_ihdr ihdr = {0};
|
||||||
|
|
||||||
|
const struct trans_stream_backend *stream_backend = NULL;
|
||||||
|
size_t encode_buf_size = 0;
|
||||||
|
uint8_t *encode_buf = NULL;
|
||||||
|
uint8_t *deflate_buf = NULL;
|
||||||
|
uint8_t *rgba_line = NULL;
|
||||||
|
uint8_t *up_filtered = NULL;
|
||||||
|
uint8_t *sub_filtered = NULL;
|
||||||
|
uint8_t *avg_filtered = NULL;
|
||||||
|
uint8_t *paeth_filtered = NULL;
|
||||||
|
uint8_t *prev_encoded = NULL;
|
||||||
|
uint8_t *encode_target = NULL;
|
||||||
|
void *stream = NULL;
|
||||||
|
uint32_t total_in = 0;
|
||||||
|
uint32_t total_out = 0;
|
||||||
|
RFILE *file = filestream_open(path,
|
||||||
|
RETRO_VFS_FILE_ACCESS_WRITE,
|
||||||
|
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||||
|
if (!file)
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
stream_backend = trans_stream_get_zlib_deflate_backend();
|
||||||
|
|
||||||
|
if (filestream_write(file, png_magic, sizeof(png_magic)) != sizeof(png_magic))
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
ihdr.width = width;
|
||||||
|
ihdr.height = height;
|
||||||
|
ihdr.depth = 8;
|
||||||
|
ihdr.color_type = bpp == sizeof(uint32_t) ? 6 : 2; /* RGBA or RGB */
|
||||||
|
if (!png_write_ihdr(file, &ihdr))
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
encode_buf_size = (width * bpp + 1) * height;
|
||||||
|
encode_buf = (uint8_t*)malloc(encode_buf_size);
|
||||||
|
if (!encode_buf)
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
prev_encoded = (uint8_t*)calloc(1, width * bpp);
|
||||||
|
if (!prev_encoded)
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
rgba_line = (uint8_t*)malloc(width * bpp);
|
||||||
|
up_filtered = (uint8_t*)malloc(width * bpp);
|
||||||
|
sub_filtered = (uint8_t*)malloc(width * bpp);
|
||||||
|
avg_filtered = (uint8_t*)malloc(width * bpp);
|
||||||
|
paeth_filtered = (uint8_t*)malloc(width * bpp);
|
||||||
|
if (!rgba_line || !up_filtered || !sub_filtered || !avg_filtered || !paeth_filtered)
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
encode_target = encode_buf;
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, encode_target += width * bpp, data += pitch)
|
||||||
|
{
|
||||||
|
if (bpp == sizeof(uint32_t))
|
||||||
|
copy_argb_line(rgba_line, (const uint32_t*)data, width);
|
||||||
|
else
|
||||||
|
copy_bgr24_line(rgba_line, data, width);
|
||||||
|
|
||||||
|
/* Try every filtering method, and choose the method
|
||||||
|
* which has most entries as zero.
|
||||||
|
*
|
||||||
|
* This is probably not very optimal, but it's very
|
||||||
|
* simple to implement.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned none_score = count_sad(rgba_line, width * bpp);
|
||||||
|
unsigned up_score = filter_up(up_filtered, rgba_line, prev_encoded, width, bpp);
|
||||||
|
unsigned sub_score = filter_sub(sub_filtered, rgba_line, width, bpp);
|
||||||
|
unsigned avg_score = filter_avg(avg_filtered, rgba_line, prev_encoded, width, bpp);
|
||||||
|
unsigned paeth_score = filter_paeth(paeth_filtered, rgba_line, prev_encoded, width, bpp);
|
||||||
|
|
||||||
|
uint8_t filter = 0;
|
||||||
|
unsigned min_sad = none_score;
|
||||||
|
const uint8_t *chosen_filtered = rgba_line;
|
||||||
|
|
||||||
|
if (sub_score < min_sad)
|
||||||
|
{
|
||||||
|
filter = 1;
|
||||||
|
chosen_filtered = sub_filtered;
|
||||||
|
min_sad = sub_score;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (up_score < min_sad)
|
||||||
|
{
|
||||||
|
filter = 2;
|
||||||
|
chosen_filtered = up_filtered;
|
||||||
|
min_sad = up_score;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avg_score < min_sad)
|
||||||
|
{
|
||||||
|
filter = 3;
|
||||||
|
chosen_filtered = avg_filtered;
|
||||||
|
min_sad = avg_score;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paeth_score < min_sad)
|
||||||
|
{
|
||||||
|
filter = 4;
|
||||||
|
chosen_filtered = paeth_filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
*encode_target++ = filter;
|
||||||
|
memcpy(encode_target, chosen_filtered, width * bpp);
|
||||||
|
|
||||||
|
memcpy(prev_encoded, rgba_line, width * bpp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deflate_buf = (uint8_t*)malloc(encode_buf_size * 2); /* Just to be sure. */
|
||||||
|
if (!deflate_buf)
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
stream = stream_backend->stream_new();
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
stream_backend->set_in(
|
||||||
|
stream,
|
||||||
|
encode_buf,
|
||||||
|
(unsigned)encode_buf_size);
|
||||||
|
stream_backend->set_out(
|
||||||
|
stream,
|
||||||
|
deflate_buf + 8,
|
||||||
|
(unsigned)(encode_buf_size * 2));
|
||||||
|
|
||||||
|
if (!stream_backend->trans(stream, true, &total_in, &total_out, NULL))
|
||||||
|
{
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(deflate_buf + 4, "IDAT", 4);
|
||||||
|
dword_write_be(deflate_buf + 0, ((uint32_t)total_out));
|
||||||
|
if (!png_write_idat(file, deflate_buf, ((size_t)total_out + 8)))
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
if (!png_write_iend(file))
|
||||||
|
GOTO_END_ERROR();
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (file)
|
||||||
|
filestream_close(file);
|
||||||
|
free(encode_buf);
|
||||||
|
free(deflate_buf);
|
||||||
|
free(rgba_line);
|
||||||
|
free(prev_encoded);
|
||||||
|
free(up_filtered);
|
||||||
|
free(sub_filtered);
|
||||||
|
free(avg_filtered);
|
||||||
|
free(paeth_filtered);
|
||||||
|
|
||||||
|
if (stream_backend)
|
||||||
|
{
|
||||||
|
if (stream)
|
||||||
|
{
|
||||||
|
if (stream_backend->stream_free)
|
||||||
|
stream_backend->stream_free(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpng_save_image_argb(const char *path, const uint32_t *data,
|
||||||
|
unsigned width, unsigned height, unsigned pitch)
|
||||||
|
{
|
||||||
|
return rpng_save_image(path, (const uint8_t*)data,
|
||||||
|
width, height, pitch, sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpng_save_image_bgr24(const char *path, const uint8_t *data,
|
||||||
|
unsigned width, unsigned height, unsigned pitch)
|
||||||
|
{
|
||||||
|
return rpng_save_image(path, (const uint8_t*)data,
|
||||||
|
width, height, pitch, 3);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rpng_internal.h).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RPNG_COMMON_H
|
||||||
|
#define _RPNG_COMMON_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <filters.h>
|
||||||
|
#include <formats/rpng.h>
|
||||||
|
|
||||||
|
#undef GOTO_END_ERROR
|
||||||
|
#define GOTO_END_ERROR() do { \
|
||||||
|
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); \
|
||||||
|
ret = false; \
|
||||||
|
goto end; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#ifndef ARRAY_SIZE
|
||||||
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const uint8_t png_magic[8] = {
|
||||||
|
0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct png_ihdr
|
||||||
|
{
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint8_t depth;
|
||||||
|
uint8_t color_type;
|
||||||
|
uint8_t compression;
|
||||||
|
uint8_t filter;
|
||||||
|
uint8_t interlace;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,446 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rtga.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Modified version of stb_image's TGA sources. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h> /* ptrdiff_t on osx */
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_assert.h>
|
||||||
|
#include <retro_inline.h>
|
||||||
|
|
||||||
|
#include <formats/image.h>
|
||||||
|
#include <formats/rtga.h>
|
||||||
|
|
||||||
|
#define RTGA_COMPUTE_Y(r, g, b) ((uint8_t)((((r) * 77) + ((g) * 150) + (29 * (b))) >> 8))
|
||||||
|
|
||||||
|
|
||||||
|
struct rtga
|
||||||
|
{
|
||||||
|
uint8_t *buff_data;
|
||||||
|
uint32_t *output_image;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t img_x, img_y;
|
||||||
|
int img_n, img_out_n;
|
||||||
|
|
||||||
|
int buflen;
|
||||||
|
uint8_t buffer_start[128];
|
||||||
|
|
||||||
|
uint8_t *img_buffer;
|
||||||
|
uint8_t *img_buffer_end;
|
||||||
|
uint8_t *img_buffer_original;
|
||||||
|
} rtga__context;
|
||||||
|
|
||||||
|
static INLINE uint8_t rtga__get8(rtga__context *s)
|
||||||
|
{
|
||||||
|
if (s->img_buffer < s->img_buffer_end)
|
||||||
|
return *s->img_buffer++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtga__skip(rtga__context *s, int n)
|
||||||
|
{
|
||||||
|
if (n < 0)
|
||||||
|
{
|
||||||
|
s->img_buffer = s->img_buffer_end;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s->img_buffer += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtga__getn(rtga__context *s, uint8_t *buffer, int n)
|
||||||
|
{
|
||||||
|
if (s->img_buffer+n <= s->img_buffer_end)
|
||||||
|
{
|
||||||
|
memcpy(buffer, s->img_buffer, n);
|
||||||
|
s->img_buffer += n;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtga__get16le(rtga__context *s)
|
||||||
|
{
|
||||||
|
int z = rtga__get8(s);
|
||||||
|
return z + (rtga__get8(s) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *rtga__convert_format(
|
||||||
|
unsigned char *data,
|
||||||
|
int img_n,
|
||||||
|
int req_comp,
|
||||||
|
unsigned int x,
|
||||||
|
unsigned int y)
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
unsigned char *good = (unsigned char *) malloc(req_comp * x * y);
|
||||||
|
|
||||||
|
if (!good)
|
||||||
|
{
|
||||||
|
free(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j=0; j < (int) y; ++j)
|
||||||
|
{
|
||||||
|
unsigned char *src = data + j * x * img_n ;
|
||||||
|
unsigned char *dest = good + j * x * req_comp;
|
||||||
|
|
||||||
|
switch (((img_n)*8+(req_comp)))
|
||||||
|
{
|
||||||
|
case ((1)*8+(2)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 1, dest += 2)
|
||||||
|
dest[0]=src[0], dest[1]=255;
|
||||||
|
break;
|
||||||
|
case ((1)*8+(3)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 1, dest += 3)
|
||||||
|
dest[0]=dest[1]=dest[2]=src[0];
|
||||||
|
break;
|
||||||
|
case ((1)*8+(4)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 1, dest += 4)
|
||||||
|
dest[0]=dest[1]=dest[2]=src[0], dest[3]=255;
|
||||||
|
break;
|
||||||
|
case ((2)*8+(1)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 2, dest += 1)
|
||||||
|
dest[0]=src[0];
|
||||||
|
break;
|
||||||
|
case ((2)*8+(3)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 2, dest += 3)
|
||||||
|
dest[0]=dest[1]=dest[2]=src[0];
|
||||||
|
break;
|
||||||
|
case ((2)*8+(4)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 2, dest += 4)
|
||||||
|
dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];
|
||||||
|
break;
|
||||||
|
case ((3)*8+(4)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 3, dest += 4)
|
||||||
|
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255;
|
||||||
|
break;
|
||||||
|
case ((3)*8+(1)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 3, dest += 1)
|
||||||
|
dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]);
|
||||||
|
break;
|
||||||
|
case ((3)*8+(2)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 3, dest += 2)
|
||||||
|
dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = 255;
|
||||||
|
break;
|
||||||
|
case ((4)*8+(1)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 4, dest += 1)
|
||||||
|
dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]);
|
||||||
|
break;
|
||||||
|
case ((4)*8+(2)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 4, dest += 2)
|
||||||
|
dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = src[3];
|
||||||
|
break;
|
||||||
|
case ((4)*8+(3)):
|
||||||
|
for(i=x-1; i >= 0; --i, src += 4, dest += 3)
|
||||||
|
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
return good;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *rtga__tga_load(rtga__context *s,
|
||||||
|
unsigned *x, unsigned *y, int *comp, int req_comp)
|
||||||
|
{
|
||||||
|
/* Read in the TGA header stuff */
|
||||||
|
int tga_offset = rtga__get8(s);
|
||||||
|
int tga_indexed = rtga__get8(s);
|
||||||
|
int tga_image_type = rtga__get8(s);
|
||||||
|
int tga_is_RLE = 0;
|
||||||
|
int tga_palette_start = rtga__get16le(s);
|
||||||
|
int tga_palette_len = rtga__get16le(s);
|
||||||
|
int tga_palette_bits = rtga__get8(s);
|
||||||
|
int tga_x_origin = rtga__get16le(s);
|
||||||
|
int tga_y_origin = rtga__get16le(s);
|
||||||
|
int tga_width = rtga__get16le(s);
|
||||||
|
int tga_height = rtga__get16le(s);
|
||||||
|
int tga_bits_per_pixel = rtga__get8(s);
|
||||||
|
int tga_comp = tga_bits_per_pixel / 8;
|
||||||
|
int tga_inverted = rtga__get8(s);
|
||||||
|
|
||||||
|
/* image data */
|
||||||
|
unsigned char *tga_data = NULL;
|
||||||
|
|
||||||
|
(void)tga_palette_start;
|
||||||
|
(void)tga_x_origin;
|
||||||
|
(void)tga_y_origin;
|
||||||
|
|
||||||
|
/* do a tiny bit of precessing */
|
||||||
|
if ( tga_image_type >= 8 )
|
||||||
|
{
|
||||||
|
tga_image_type -= 8;
|
||||||
|
tga_is_RLE = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* int tga_alpha_bits = tga_inverted & 15; */
|
||||||
|
tga_inverted = 1 - ((tga_inverted >> 5) & 1);
|
||||||
|
|
||||||
|
/* error check */
|
||||||
|
if (
|
||||||
|
(tga_width < 1) || (tga_height < 1) ||
|
||||||
|
(tga_image_type < 1) || (tga_image_type > 3) ||
|
||||||
|
(
|
||||||
|
(tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
|
||||||
|
(tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return NULL; /* we don't report this as a bad TGA because we don't even know if it's TGA */
|
||||||
|
|
||||||
|
/* If paletted, then we will use the number of bits from the palette */
|
||||||
|
if ( tga_indexed )
|
||||||
|
tga_comp = tga_palette_bits / 8;
|
||||||
|
|
||||||
|
/* TGA info */
|
||||||
|
*x = tga_width;
|
||||||
|
*y = tga_height;
|
||||||
|
if (comp) *comp = tga_comp;
|
||||||
|
|
||||||
|
tga_data = (unsigned char*)malloc( (size_t)tga_width * tga_height * tga_comp );
|
||||||
|
if (!tga_data)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* skip to the data's starting position (offset usually = 0) */
|
||||||
|
rtga__skip(s, tga_offset );
|
||||||
|
|
||||||
|
if ( !tga_indexed && !tga_is_RLE)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < tga_height; ++i)
|
||||||
|
{
|
||||||
|
int y = tga_inverted ? tga_height -i - 1 : i;
|
||||||
|
uint8_t *tga_row = tga_data + y*tga_width*tga_comp;
|
||||||
|
rtga__getn(s, tga_row, tga_width * tga_comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
int RLE_repeating = 0;
|
||||||
|
int RLE_count = 0;
|
||||||
|
int read_next_pixel = 1;
|
||||||
|
unsigned char raw_data[4] = {0};
|
||||||
|
unsigned char *tga_palette = NULL;
|
||||||
|
|
||||||
|
/* Do I need to load a palette? */
|
||||||
|
if ( tga_indexed)
|
||||||
|
{
|
||||||
|
/* any data to skip? (offset usually = 0) */
|
||||||
|
rtga__skip(s, tga_palette_start );
|
||||||
|
/* load the palette */
|
||||||
|
tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 );
|
||||||
|
|
||||||
|
if (!tga_palette)
|
||||||
|
{
|
||||||
|
free(tga_data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rtga__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 ))
|
||||||
|
{
|
||||||
|
free(tga_data);
|
||||||
|
free(tga_palette);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load the data */
|
||||||
|
for (i=0; i < tga_width * tga_height; ++i)
|
||||||
|
{
|
||||||
|
/* if I'm in RLE mode, do I need to get a RLE rtga__pngchunk? */
|
||||||
|
if ( tga_is_RLE )
|
||||||
|
{
|
||||||
|
if ( RLE_count == 0 )
|
||||||
|
{
|
||||||
|
/* yep, get the next byte as a RLE command */
|
||||||
|
int RLE_cmd = rtga__get8(s);
|
||||||
|
RLE_count = 1 + (RLE_cmd & 127);
|
||||||
|
RLE_repeating = RLE_cmd >> 7;
|
||||||
|
read_next_pixel = 1;
|
||||||
|
}
|
||||||
|
else if ( !RLE_repeating )
|
||||||
|
read_next_pixel = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
read_next_pixel = 1;
|
||||||
|
|
||||||
|
/* OK, if I need to read a pixel, do it now */
|
||||||
|
if ( read_next_pixel )
|
||||||
|
{
|
||||||
|
/* load however much data we did have */
|
||||||
|
if ( tga_indexed )
|
||||||
|
{
|
||||||
|
/* read in 1 byte, then perform the lookup */
|
||||||
|
int pal_idx = rtga__get8(s);
|
||||||
|
if ( pal_idx >= tga_palette_len ) /* invalid index */
|
||||||
|
pal_idx = 0;
|
||||||
|
pal_idx *= tga_bits_per_pixel / 8;
|
||||||
|
for (j = 0; j*8 < tga_bits_per_pixel; ++j)
|
||||||
|
raw_data[j] = tga_palette[pal_idx+j];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* read in the data raw */
|
||||||
|
for (j = 0; j*8 < tga_bits_per_pixel; ++j)
|
||||||
|
raw_data[j] = rtga__get8(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear the reading flag for the next pixel */
|
||||||
|
read_next_pixel = 0;
|
||||||
|
} /* end of reading a pixel */
|
||||||
|
|
||||||
|
/* copy data */
|
||||||
|
for (j = 0; j < tga_comp; ++j)
|
||||||
|
tga_data[i*tga_comp+j] = raw_data[j];
|
||||||
|
|
||||||
|
/* in case we're in RLE mode, keep counting down */
|
||||||
|
--RLE_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do I need to invert the image? */
|
||||||
|
if ( tga_inverted )
|
||||||
|
{
|
||||||
|
for (j = 0; j*2 < tga_height; ++j)
|
||||||
|
{
|
||||||
|
int index1 = j * tga_width * tga_comp;
|
||||||
|
int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
|
||||||
|
|
||||||
|
for (i = tga_width * tga_comp; i > 0; --i)
|
||||||
|
{
|
||||||
|
unsigned char temp = tga_data[index1];
|
||||||
|
tga_data[index1] = tga_data[index2];
|
||||||
|
tga_data[index2] = temp;
|
||||||
|
++index1;
|
||||||
|
++index2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear my palette, if I had one */
|
||||||
|
if ( tga_palette != NULL )
|
||||||
|
free( tga_palette );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* swap RGB */
|
||||||
|
if (tga_comp >= 3)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char* tga_pixel = tga_data;
|
||||||
|
|
||||||
|
for (i = 0; i < tga_width * tga_height; ++i)
|
||||||
|
{
|
||||||
|
unsigned char temp = tga_pixel[0];
|
||||||
|
tga_pixel[0] = tga_pixel[2];
|
||||||
|
tga_pixel[2] = temp;
|
||||||
|
tga_pixel += tga_comp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert to target component count */
|
||||||
|
if ( (req_comp)
|
||||||
|
&& (req_comp >= 1 && req_comp <= 4)
|
||||||
|
&& (req_comp != tga_comp))
|
||||||
|
{
|
||||||
|
tga_data = rtga__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tga_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *rtga_load_from_memory(uint8_t const *buffer, int len,
|
||||||
|
unsigned *x, unsigned *y, int *comp, int req_comp)
|
||||||
|
{
|
||||||
|
rtga__context s;
|
||||||
|
|
||||||
|
s.img_buffer = (uint8_t *)buffer;
|
||||||
|
s.img_buffer_original = (uint8_t *) buffer;
|
||||||
|
s.img_buffer_end = (uint8_t *) buffer+len;
|
||||||
|
|
||||||
|
return rtga__tga_load(&s,x,y,comp,req_comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtga_process_image(rtga_t *rtga, void **buf_data,
|
||||||
|
size_t size, unsigned *width, unsigned *height)
|
||||||
|
{
|
||||||
|
int comp;
|
||||||
|
unsigned size_tex = 0;
|
||||||
|
|
||||||
|
if (!rtga)
|
||||||
|
return IMAGE_PROCESS_ERROR;
|
||||||
|
|
||||||
|
rtga->output_image = (uint32_t*)rtga_load_from_memory(rtga->buff_data,
|
||||||
|
(int)size, width, height, &comp, 4);
|
||||||
|
*buf_data = rtga->output_image;
|
||||||
|
size_tex = (*width) * (*height);
|
||||||
|
|
||||||
|
/* Convert RGBA to ARGB */
|
||||||
|
while(size_tex--)
|
||||||
|
{
|
||||||
|
unsigned int texel = rtga->output_image[size_tex];
|
||||||
|
unsigned int A = texel & 0xFF000000;
|
||||||
|
unsigned int B = texel & 0x00FF0000;
|
||||||
|
unsigned int G = texel & 0x0000FF00;
|
||||||
|
unsigned int R = texel & 0x000000FF;
|
||||||
|
((unsigned int*)rtga->output_image)[size_tex] = A | (R << 16) | G | (B >> 16);
|
||||||
|
};
|
||||||
|
|
||||||
|
return IMAGE_PROCESS_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rtga_set_buf_ptr(rtga_t *rtga, void *data)
|
||||||
|
{
|
||||||
|
if (!rtga)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rtga->buff_data = (uint8_t*)data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtga_free(rtga_t *rtga)
|
||||||
|
{
|
||||||
|
if (!rtga)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(rtga);
|
||||||
|
}
|
||||||
|
|
||||||
|
rtga_t *rtga_alloc(void)
|
||||||
|
{
|
||||||
|
rtga_t *rtga = (rtga_t*)calloc(1, sizeof(*rtga));
|
||||||
|
if (!rtga)
|
||||||
|
return NULL;
|
||||||
|
return rtga;
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rwav.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h> /* ptrdiff_t on osx */
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <formats/rwav.h>
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ITER_BEGIN,
|
||||||
|
ITER_COPY_SAMPLES,
|
||||||
|
ITER_COPY_SAMPLES_8,
|
||||||
|
ITER_COPY_SAMPLES_16
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rwav_iterator
|
||||||
|
{
|
||||||
|
rwav_t *out;
|
||||||
|
const uint8_t *data;
|
||||||
|
size_t size;
|
||||||
|
size_t i, j;
|
||||||
|
int step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void rwav_init(rwav_iterator_t* iter, rwav_t* out, const void* buf, size_t size)
|
||||||
|
{
|
||||||
|
iter->out = out;
|
||||||
|
iter->data = (const uint8_t*)buf;
|
||||||
|
iter->size = size;
|
||||||
|
iter->step = ITER_BEGIN;
|
||||||
|
|
||||||
|
out->samples = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum rwav_state rwav_iterate(rwav_iterator_t *iter)
|
||||||
|
{
|
||||||
|
size_t s;
|
||||||
|
uint16_t *u16 = NULL;
|
||||||
|
void *samples = NULL;
|
||||||
|
rwav_t *rwav = iter->out;
|
||||||
|
const uint8_t *data = iter->data;
|
||||||
|
|
||||||
|
switch (iter->step)
|
||||||
|
{
|
||||||
|
case ITER_BEGIN:
|
||||||
|
if (iter->size < 44)
|
||||||
|
return RWAV_ITERATE_ERROR; /* buffer is smaller than an empty wave file */
|
||||||
|
|
||||||
|
if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F')
|
||||||
|
return RWAV_ITERATE_ERROR;
|
||||||
|
|
||||||
|
if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E')
|
||||||
|
return RWAV_ITERATE_ERROR;
|
||||||
|
|
||||||
|
if (data[12] != 'f' || data[13] != 'm' || data[14] != 't' || data[15] != ' ')
|
||||||
|
return RWAV_ITERATE_ERROR; /* we don't support non-PCM or compressed data */
|
||||||
|
|
||||||
|
if (data[16] != 16 || data[17] != 0 || data[18] != 0 || data[19] != 0)
|
||||||
|
return RWAV_ITERATE_ERROR;
|
||||||
|
|
||||||
|
if (data[20] != 1 || data[21] != 0)
|
||||||
|
return RWAV_ITERATE_ERROR; /* we don't support non-PCM or compressed data */
|
||||||
|
|
||||||
|
if (data[36] != 'd' || data[37] != 'a' || data[38] != 't' || data[39] != 'a')
|
||||||
|
return RWAV_ITERATE_ERROR;
|
||||||
|
|
||||||
|
rwav->bitspersample = data[34] | data[35] << 8;
|
||||||
|
|
||||||
|
if (rwav->bitspersample != 8 && rwav->bitspersample != 16)
|
||||||
|
return RWAV_ITERATE_ERROR; /* we only support 8 and 16 bps */
|
||||||
|
|
||||||
|
rwav->subchunk2size = data[40] | data[41] << 8 | data[42] << 16 | data[43] << 24;
|
||||||
|
|
||||||
|
if (rwav->subchunk2size > iter->size - 44)
|
||||||
|
return RWAV_ITERATE_ERROR; /* too few bytes in buffer */
|
||||||
|
|
||||||
|
samples = malloc(rwav->subchunk2size);
|
||||||
|
|
||||||
|
if (samples == NULL)
|
||||||
|
return RWAV_ITERATE_ERROR;
|
||||||
|
|
||||||
|
rwav->numchannels = data[22] | data[23] << 8;
|
||||||
|
rwav->numsamples = rwav->subchunk2size * 8 / rwav->bitspersample / rwav->numchannels;
|
||||||
|
rwav->samplerate = data[24] | data[25] << 8 | data[26] << 16 | data[27] << 24;
|
||||||
|
rwav->samples = samples;
|
||||||
|
|
||||||
|
iter->step = ITER_COPY_SAMPLES;
|
||||||
|
return RWAV_ITERATE_MORE;
|
||||||
|
|
||||||
|
case ITER_COPY_SAMPLES:
|
||||||
|
iter->i = 0;
|
||||||
|
|
||||||
|
if (rwav->bitspersample == 8)
|
||||||
|
{
|
||||||
|
iter->step = ITER_COPY_SAMPLES_8;
|
||||||
|
|
||||||
|
/* TODO/FIXME - what is going on here? */
|
||||||
|
case ITER_COPY_SAMPLES_8:
|
||||||
|
s = rwav->subchunk2size - iter->i;
|
||||||
|
|
||||||
|
if (s > RWAV_ITERATE_BUF_SIZE)
|
||||||
|
s = RWAV_ITERATE_BUF_SIZE;
|
||||||
|
|
||||||
|
memcpy((void*)((uint8_t*)rwav->samples + iter->i), (void *)(iter->data + 44 + iter->i), s);
|
||||||
|
iter->i += s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->step = ITER_COPY_SAMPLES_16;
|
||||||
|
iter->j = 0;
|
||||||
|
|
||||||
|
/* TODO/FIXME - what is going on here? */
|
||||||
|
case ITER_COPY_SAMPLES_16:
|
||||||
|
s = rwav->subchunk2size - iter->i;
|
||||||
|
|
||||||
|
if (s > RWAV_ITERATE_BUF_SIZE)
|
||||||
|
s = RWAV_ITERATE_BUF_SIZE;
|
||||||
|
|
||||||
|
u16 = (uint16_t *)rwav->samples;
|
||||||
|
|
||||||
|
while (s != 0)
|
||||||
|
{
|
||||||
|
u16[iter->j++] = iter->data[44 + iter->i] | iter->data[45 + iter->i] << 8;
|
||||||
|
iter->i += 2;
|
||||||
|
s -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iter->i < rwav->subchunk2size)
|
||||||
|
return RWAV_ITERATE_MORE;
|
||||||
|
return RWAV_ITERATE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RWAV_ITERATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum rwav_state rwav_load(rwav_t* out, const void* buf, size_t size)
|
||||||
|
{
|
||||||
|
enum rwav_state res;
|
||||||
|
rwav_iterator_t iter;
|
||||||
|
|
||||||
|
iter.out = NULL;
|
||||||
|
iter.data = NULL;
|
||||||
|
iter.size = 0;
|
||||||
|
iter.i = 0;
|
||||||
|
iter.j = 0;
|
||||||
|
iter.step = 0;
|
||||||
|
|
||||||
|
rwav_init(&iter, out, buf, size);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
res = rwav_iterate(&iter);
|
||||||
|
}while (res == RWAV_ITERATE_MORE);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rwav_free(rwav_t *rwav)
|
||||||
|
{
|
||||||
|
free((void*)rwav->samples);
|
||||||
|
}
|
|
@ -0,0 +1,488 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rxml.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <boolean.h>
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <compat/posix_string.h>
|
||||||
|
#include <string/stdstring.h>
|
||||||
|
|
||||||
|
#include <formats/rxml.h>
|
||||||
|
|
||||||
|
struct rxml_document
|
||||||
|
{
|
||||||
|
struct rxml_node *root_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rxml_node *rxml_root_node(rxml_document_t *doc)
|
||||||
|
{
|
||||||
|
if (doc)
|
||||||
|
return doc->root_node;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rxml_free_node(struct rxml_node *node)
|
||||||
|
{
|
||||||
|
struct rxml_node *head = NULL;
|
||||||
|
struct rxml_attrib_node *attrib_node_head = NULL;
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (head = node->children; head; )
|
||||||
|
{
|
||||||
|
struct rxml_node *next_node = (struct rxml_node*)head->next;
|
||||||
|
rxml_free_node(head);
|
||||||
|
head = next_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (attrib_node_head = node->attrib; attrib_node_head; )
|
||||||
|
{
|
||||||
|
struct rxml_attrib_node *next_attrib = NULL;
|
||||||
|
|
||||||
|
if (!attrib_node_head)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
next_attrib = (struct rxml_attrib_node*)attrib_node_head->next;
|
||||||
|
|
||||||
|
if (!next_attrib)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (attrib_node_head->attrib)
|
||||||
|
free(attrib_node_head->attrib);
|
||||||
|
if (attrib_node_head->value)
|
||||||
|
free(attrib_node_head->value);
|
||||||
|
if (attrib_node_head)
|
||||||
|
free(attrib_node_head);
|
||||||
|
|
||||||
|
attrib_node_head = next_attrib;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->name)
|
||||||
|
free(node->name);
|
||||||
|
if (node->data)
|
||||||
|
free(node->data);
|
||||||
|
if (node)
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool validate_header(const char **ptr)
|
||||||
|
{
|
||||||
|
if (memcmp(*ptr, "<?xml", 5) == 0)
|
||||||
|
{
|
||||||
|
const char *eol = strstr(*ptr, "?>\n");
|
||||||
|
if (!eol)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Always use UTF-8. Don't really care to check. */
|
||||||
|
*ptr = eol + 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool range_is_space(const char *begin, const char *end)
|
||||||
|
{
|
||||||
|
for (; begin < end; begin++)
|
||||||
|
if (!isspace(*begin))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void skip_spaces(const char **ptr_)
|
||||||
|
{
|
||||||
|
const char *ptr = *ptr_;
|
||||||
|
while (isspace(*ptr))
|
||||||
|
ptr++;
|
||||||
|
|
||||||
|
*ptr_ = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *strdup_range(const char *begin, const char *end)
|
||||||
|
{
|
||||||
|
ptrdiff_t len = end - begin;
|
||||||
|
char *ret = (char*)malloc(len + 1);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(ret, begin, len);
|
||||||
|
ret[len] = '\0';
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *strdup_range_escape(const char *begin, const char *end)
|
||||||
|
{
|
||||||
|
/* Escaping is ignored. Assume we don't deal with that. */
|
||||||
|
return strdup_range(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rxml_attrib_node *rxml_parse_attrs(const char *str)
|
||||||
|
{
|
||||||
|
char *copy = strdup(str);
|
||||||
|
if (!copy)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *last_char = copy + strlen(copy) - 1;
|
||||||
|
if (*last_char == '/')
|
||||||
|
*last_char = '\0';
|
||||||
|
|
||||||
|
struct rxml_attrib_node *list = NULL;
|
||||||
|
struct rxml_attrib_node *tail = NULL;
|
||||||
|
|
||||||
|
char *attrib = NULL;
|
||||||
|
char *value = NULL;
|
||||||
|
char *save;
|
||||||
|
const char *elem = strtok_r(copy, " \n\t\f\v\r", &save);
|
||||||
|
while (elem)
|
||||||
|
{
|
||||||
|
const char *eq = strstr(elem, "=\"");
|
||||||
|
if (!eq)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
const char *end = strrchr(eq + 2, '\"');
|
||||||
|
if (!end || end != (elem + strlen(elem) - 1))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
attrib = strdup_range_escape(elem, eq);
|
||||||
|
value = strdup_range_escape(eq + 2, end);
|
||||||
|
if (!attrib || !value)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
struct rxml_attrib_node *new_node =
|
||||||
|
(struct rxml_attrib_node*)calloc(1, sizeof(*new_node));
|
||||||
|
if (!new_node)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
new_node->attrib = attrib;
|
||||||
|
new_node->value = value;
|
||||||
|
attrib = NULL;
|
||||||
|
value = NULL;
|
||||||
|
|
||||||
|
if (tail)
|
||||||
|
{
|
||||||
|
tail->next = new_node;
|
||||||
|
tail = new_node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
list = tail = new_node;
|
||||||
|
|
||||||
|
elem = strtok_r(NULL, " \n\t\f\v\r", &save);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (copy)
|
||||||
|
free(copy);
|
||||||
|
if (attrib)
|
||||||
|
free(attrib);
|
||||||
|
if (value)
|
||||||
|
free(value);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *find_first_space(const char *str)
|
||||||
|
{
|
||||||
|
while (*str && !isspace(*str))
|
||||||
|
str++;
|
||||||
|
|
||||||
|
return isspace(*str) ? (char*)str : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rxml_parse_tag(struct rxml_node *node, const char *str)
|
||||||
|
{
|
||||||
|
const char *str_ptr = str;
|
||||||
|
skip_spaces(&str_ptr);
|
||||||
|
|
||||||
|
const char *name_end = find_first_space(str_ptr);
|
||||||
|
if (name_end)
|
||||||
|
{
|
||||||
|
node->name = strdup_range(str_ptr, name_end);
|
||||||
|
if (!node->name || !*node->name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
node->attrib = rxml_parse_attrs(name_end);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node->name = strdup(str_ptr);
|
||||||
|
return node->name && *node->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rxml_node *rxml_parse_node(const char **ptr_)
|
||||||
|
{
|
||||||
|
const char *ptr = NULL;
|
||||||
|
const char *closing = NULL;
|
||||||
|
char *str = NULL;
|
||||||
|
bool is_closing = false;
|
||||||
|
|
||||||
|
struct rxml_node *node = (struct rxml_node*)calloc(1, sizeof(*node));
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
skip_spaces(ptr_);
|
||||||
|
|
||||||
|
ptr = *ptr_;
|
||||||
|
if (*ptr != '<')
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
closing = strchr(ptr, '>');
|
||||||
|
if (!closing)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
str = strdup_range(ptr + 1, closing);
|
||||||
|
if (!str)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!rxml_parse_tag(node, str))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Are spaces between / and > allowed? */
|
||||||
|
is_closing = strstr(ptr, "/>") + 1 == closing;
|
||||||
|
|
||||||
|
/* Look for more data. Either child nodes or data. */
|
||||||
|
if (!is_closing)
|
||||||
|
{
|
||||||
|
size_t closing_tag_size = strlen(node->name) + 4;
|
||||||
|
char *closing_tag = (char*)malloc(closing_tag_size);
|
||||||
|
|
||||||
|
const char *cdata_start = NULL;
|
||||||
|
const char *child_start = NULL;
|
||||||
|
const char *closing_start = NULL;
|
||||||
|
|
||||||
|
if (!closing_tag)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
snprintf(closing_tag, closing_tag_size, "</%s>", node->name);
|
||||||
|
|
||||||
|
cdata_start = strstr(closing + 1, "<![CDATA[");
|
||||||
|
child_start = strchr(closing + 1, '<');
|
||||||
|
closing_start = strstr(closing + 1, closing_tag);
|
||||||
|
|
||||||
|
if (!closing_start)
|
||||||
|
{
|
||||||
|
free(closing_tag);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cdata_start && range_is_space(closing + 1, cdata_start))
|
||||||
|
{
|
||||||
|
/* CDATA section */
|
||||||
|
const char *cdata_end = strstr(cdata_start, "]]>");
|
||||||
|
if (!cdata_end)
|
||||||
|
{
|
||||||
|
free(closing_tag);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->data = strdup_range(cdata_start +
|
||||||
|
strlen("<![CDATA["), cdata_end);
|
||||||
|
}
|
||||||
|
else if (closing_start && closing_start == child_start) /* Simple Data */
|
||||||
|
node->data = strdup_range(closing + 1, closing_start);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Parse all child nodes. */
|
||||||
|
struct rxml_node *list = NULL;
|
||||||
|
struct rxml_node *tail = NULL;
|
||||||
|
const char *first_start = NULL;
|
||||||
|
const char *first_closing = NULL;
|
||||||
|
|
||||||
|
ptr = child_start;
|
||||||
|
first_start = strchr(ptr, '<');
|
||||||
|
first_closing = strstr(ptr, "</");
|
||||||
|
|
||||||
|
while (
|
||||||
|
first_start &&
|
||||||
|
first_closing &&
|
||||||
|
(first_start < first_closing)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
struct rxml_node *new_node = rxml_parse_node(&ptr);
|
||||||
|
|
||||||
|
if (!new_node)
|
||||||
|
{
|
||||||
|
free(closing_tag);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tail)
|
||||||
|
{
|
||||||
|
tail->next = new_node;
|
||||||
|
tail = new_node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
list = tail = new_node;
|
||||||
|
|
||||||
|
first_start = strchr(ptr, '<');
|
||||||
|
first_closing = strstr(ptr, "</");
|
||||||
|
}
|
||||||
|
|
||||||
|
node->children = list;
|
||||||
|
|
||||||
|
closing_start = strstr(ptr, closing_tag);
|
||||||
|
if (!closing_start)
|
||||||
|
{
|
||||||
|
free(closing_tag);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr_ = closing_start + strlen(closing_tag);
|
||||||
|
free(closing_tag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*ptr_ = closing + 1;
|
||||||
|
|
||||||
|
if (str)
|
||||||
|
free(str);
|
||||||
|
return node;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (str)
|
||||||
|
free(str);
|
||||||
|
rxml_free_node(node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *purge_xml_comments(const char *str)
|
||||||
|
{
|
||||||
|
size_t len = strlen(str);
|
||||||
|
char *new_str = (char*)malloc(len + 1);
|
||||||
|
if (!new_str)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
new_str[len] = '\0';
|
||||||
|
|
||||||
|
char *copy_dest = new_str;
|
||||||
|
const char *copy_src = str;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
ptrdiff_t copy_len;
|
||||||
|
const char *comment_start = strstr(copy_src, "<!--");
|
||||||
|
const char *comment_end = strstr(copy_src, "-->");
|
||||||
|
|
||||||
|
if (!comment_start || !comment_end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
copy_len = comment_start - copy_src;
|
||||||
|
memcpy(copy_dest, copy_src, copy_len);
|
||||||
|
|
||||||
|
copy_dest += copy_len;
|
||||||
|
copy_src = comment_end + strlen("-->");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Avoid strcpy() as OpenBSD is anal and hates you
|
||||||
|
* for using it even when it's perfectly safe. */
|
||||||
|
len = strlen(copy_src);
|
||||||
|
memcpy(copy_dest, copy_src, len);
|
||||||
|
copy_dest[len] = '\0';
|
||||||
|
|
||||||
|
return new_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
rxml_document_t *rxml_load_document(const char *path)
|
||||||
|
{
|
||||||
|
char *memory_buffer = NULL;
|
||||||
|
char *new_memory_buffer = NULL;
|
||||||
|
const char *mem_ptr = NULL;
|
||||||
|
long len = 0;
|
||||||
|
RFILE *file = filestream_open(path,
|
||||||
|
RETRO_VFS_FILE_ACCESS_READ,
|
||||||
|
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||||
|
if (!file)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
rxml_document_t *doc = (rxml_document_t*)calloc(1, sizeof(*doc));
|
||||||
|
if (!doc)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
len = filestream_get_size(file);
|
||||||
|
memory_buffer = (char*)malloc(len + 1);
|
||||||
|
if (!memory_buffer)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
memory_buffer[len] = '\0';
|
||||||
|
if (filestream_read(file, memory_buffer, len) != (size_t)len)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
filestream_close(file);
|
||||||
|
file = NULL;
|
||||||
|
|
||||||
|
mem_ptr = memory_buffer;
|
||||||
|
|
||||||
|
if (!validate_header(&mem_ptr))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
new_memory_buffer = purge_xml_comments(mem_ptr);
|
||||||
|
if (!new_memory_buffer)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
free(memory_buffer);
|
||||||
|
mem_ptr = memory_buffer = new_memory_buffer;
|
||||||
|
|
||||||
|
doc->root_node = rxml_parse_node(&mem_ptr);
|
||||||
|
if (!doc->root_node)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
free(memory_buffer);
|
||||||
|
return doc;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(memory_buffer);
|
||||||
|
filestream_close(file);
|
||||||
|
rxml_free_document(doc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rxml_free_document(rxml_document_t *doc)
|
||||||
|
{
|
||||||
|
if (!doc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (doc->root_node)
|
||||||
|
rxml_free_node(doc->root_node);
|
||||||
|
|
||||||
|
free(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *rxml_node_attrib(struct rxml_node *node, const char *attrib)
|
||||||
|
{
|
||||||
|
struct rxml_attrib_node *attribs = NULL;
|
||||||
|
for (attribs = node->attrib; attribs; attribs = attribs->next)
|
||||||
|
{
|
||||||
|
if (string_is_equal(attrib, attribs->attrib))
|
||||||
|
return attribs->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
TARGET := rxml
|
||||||
|
|
||||||
|
LIBRETRO_XML_DIR := ..
|
||||||
|
LIBRETRO_COMM_DIR := ../../..
|
||||||
|
|
||||||
|
SOURCES := \
|
||||||
|
rxml_test.c \
|
||||||
|
$(LIBRETRO_XML_DIR)/rxml.c \
|
||||||
|
$(LIBRETRO_COMM_DIR)/streams/file_stream.c
|
||||||
|
|
||||||
|
OBJS := $(SOURCES:.c=.o)
|
||||||
|
|
||||||
|
CFLAGS += -DRXML_TEST -Wall -pedantic -std=gnu99 -g -I$(LIBRETRO_COMM_DIR)/include
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET) $(OBJS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rxml_test.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <formats/rxml.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static void print_siblings(struct rxml_node *node, unsigned level)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n%*sName: %s\n", level * 4, "", node->name);
|
||||||
|
if (node->data)
|
||||||
|
fprintf(stderr, "%*sData: %s\n", level * 4, "", node->data);
|
||||||
|
|
||||||
|
for (const struct rxml_attrib_node *attrib =
|
||||||
|
node->attrib; attrib; attrib = attrib->next)
|
||||||
|
fprintf(stderr, "%*s Attrib: %s = %s\n", level * 4, "",
|
||||||
|
attrib->attrib, attrib->value);
|
||||||
|
|
||||||
|
if (node->children)
|
||||||
|
print_siblings(node->children, level + 1);
|
||||||
|
|
||||||
|
if (node->next)
|
||||||
|
print_siblings(node->next, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rxml_log_document(const char *path)
|
||||||
|
{
|
||||||
|
rxml_document_t *doc = rxml_load_document(path);
|
||||||
|
if (!doc)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "rxml: Failed to load document: %s\n", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_siblings(rxml_root_node(doc), 0);
|
||||||
|
rxml_free_document(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s <path>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rxml_log_document(argv[1]);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,335 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (gl_capabilities.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <boolean.h>
|
||||||
|
|
||||||
|
#include <glsym/glsym.h>
|
||||||
|
|
||||||
|
#include <gfx/gl_capabilities.h>
|
||||||
|
|
||||||
|
static bool gl_core_context = false;
|
||||||
|
|
||||||
|
bool gl_query_core_context_in_use(void)
|
||||||
|
{
|
||||||
|
return gl_core_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gl_query_core_context_set(bool set)
|
||||||
|
{
|
||||||
|
gl_core_context = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gl_query_core_context_unset(void)
|
||||||
|
{
|
||||||
|
gl_core_context = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gl_query_extension(const char *ext)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (gl_query_core_context_in_use())
|
||||||
|
{
|
||||||
|
#ifdef GL_NUM_EXTENSIONS
|
||||||
|
GLint i;
|
||||||
|
GLint exts = 0;
|
||||||
|
glGetIntegerv(GL_NUM_EXTENSIONS, &exts);
|
||||||
|
for (i = 0; i < exts; i++)
|
||||||
|
{
|
||||||
|
const char *str = (const char*)glGetStringi(GL_EXTENSIONS, i);
|
||||||
|
if (str && strstr(str, ext))
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *str = (const char*)glGetString(GL_EXTENSIONS);
|
||||||
|
ret = str && strstr(str, ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gl_check_error(char **error_string)
|
||||||
|
{
|
||||||
|
int error = glGetError();
|
||||||
|
switch (error)
|
||||||
|
{
|
||||||
|
case GL_INVALID_ENUM:
|
||||||
|
*error_string = strdup("GL: Invalid enum.");
|
||||||
|
break;
|
||||||
|
case GL_INVALID_VALUE:
|
||||||
|
*error_string = strdup("GL: Invalid value.");
|
||||||
|
break;
|
||||||
|
case GL_INVALID_OPERATION:
|
||||||
|
*error_string = strdup("GL: Invalid operation.");
|
||||||
|
break;
|
||||||
|
case GL_OUT_OF_MEMORY:
|
||||||
|
*error_string = strdup("GL: Out of memory.");
|
||||||
|
break;
|
||||||
|
case GL_NO_ERROR:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
*error_string = strdup("Non specified GL error.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gl_check_capability(enum gl_capability_enum enum_idx)
|
||||||
|
{
|
||||||
|
unsigned major = 0;
|
||||||
|
unsigned minor = 0;
|
||||||
|
const char *vendor = (const char*)glGetString(GL_VENDOR);
|
||||||
|
const char *renderer = (const char*)glGetString(GL_RENDERER);
|
||||||
|
const char *version = (const char*)glGetString(GL_VERSION);
|
||||||
|
#ifdef HAVE_OPENGLES
|
||||||
|
if (version && sscanf(version, "OpenGL ES %u.%u", &major, &minor) != 2)
|
||||||
|
#else
|
||||||
|
if (version && sscanf(version, "%u.%u", &major, &minor) != 2)
|
||||||
|
#endif
|
||||||
|
major = minor = 0;
|
||||||
|
|
||||||
|
(void)vendor;
|
||||||
|
(void)renderer;
|
||||||
|
|
||||||
|
switch (enum_idx)
|
||||||
|
{
|
||||||
|
case GL_CAPS_GLES3_SUPPORTED:
|
||||||
|
#if defined(HAVE_OPENGLES)
|
||||||
|
if (major >= 3)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_EGLIMAGE:
|
||||||
|
#if defined(HAVE_EGL) && defined(HAVE_OPENGLES)
|
||||||
|
if (glEGLImageTargetTexture2DOES != NULL)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_SYNC:
|
||||||
|
#ifdef HAVE_OPENGLES
|
||||||
|
if (major >= 3)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
if (gl_query_extension("ARB_sync") &&
|
||||||
|
glFenceSync && glDeleteSync && glClientWaitSync)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_MIPMAP:
|
||||||
|
{
|
||||||
|
static bool extension_queried = false;
|
||||||
|
static bool extension = false;
|
||||||
|
|
||||||
|
if (!extension_queried)
|
||||||
|
{
|
||||||
|
extension = gl_query_extension("ARB_framebuffer_object");
|
||||||
|
extension_queried = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extension)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GL_CAPS_VAO:
|
||||||
|
#ifndef HAVE_OPENGLES
|
||||||
|
if (!gl_query_core_context_in_use() && !gl_query_extension("ARB_vertex_array_object"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (glGenVertexArrays && glBindVertexArray && glDeleteVertexArrays)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_FBO:
|
||||||
|
#if defined(HAVE_PSGL) || defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES3) || defined(HAVE_OPENGLES_3_1) || defined(HAVE_OPENGLES_3_2)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
if ( !gl_query_core_context_in_use()
|
||||||
|
&& !gl_query_extension("ARB_framebuffer_object")
|
||||||
|
&& !gl_query_extension("EXT_framebuffer_object"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (gl_query_extension("ARB_framebuffer_object"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (gl_query_extension("EXT_framebuffer_object"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (major >= 3)
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case GL_CAPS_ARGB8:
|
||||||
|
#ifdef HAVE_OPENGLES
|
||||||
|
if (gl_query_extension("OES_rgb8_rgba8")
|
||||||
|
|| gl_query_extension("ARM_rgba8")
|
||||||
|
|| major >= 3)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
/* TODO/FIXME - implement this for non-GLES? */
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_DEBUG:
|
||||||
|
if (gl_query_extension("KHR_debug"))
|
||||||
|
return true;
|
||||||
|
#ifndef HAVE_OPENGLES
|
||||||
|
if (gl_query_extension("ARB_debug_output"))
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_PACKED_DEPTH_STENCIL:
|
||||||
|
if (major >= 3)
|
||||||
|
return true;
|
||||||
|
if (gl_query_extension("OES_packed_depth_stencil"))
|
||||||
|
return true;
|
||||||
|
if (gl_query_extension("EXT_packed_depth_stencil"))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case GL_CAPS_ES2_COMPAT:
|
||||||
|
#ifndef HAVE_OPENGLES
|
||||||
|
/* ATI card detected, skipping check for GL_RGB565 support... */
|
||||||
|
if (vendor && renderer && (strstr(vendor, "ATI") || strstr(renderer, "ATI")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (gl_query_extension("ARB_ES2_compatibility"))
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_UNPACK_ROW_LENGTH:
|
||||||
|
#ifdef HAVE_OPENGLES
|
||||||
|
if (major >= 3)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* Extension GL_EXT_unpack_subimage, can copy textures faster
|
||||||
|
* than using UNPACK_ROW_LENGTH */
|
||||||
|
if (gl_query_extension("GL_EXT_unpack_subimage"))
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_FULL_NPOT_SUPPORT:
|
||||||
|
if (major >= 3)
|
||||||
|
return true;
|
||||||
|
#ifdef HAVE_OPENGLES
|
||||||
|
if (gl_query_extension("ARB_texture_non_power_of_two") ||
|
||||||
|
gl_query_extension("OES_texture_npot"))
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
GLint max_texture_size = 0;
|
||||||
|
GLint max_native_instr = 0;
|
||||||
|
/* try to detect actual npot support. might fail for older cards. */
|
||||||
|
bool arb_npot = gl_query_extension("ARB_texture_non_power_of_two");
|
||||||
|
bool arb_frag_program = gl_query_extension("ARB_fragment_program");
|
||||||
|
|
||||||
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
|
||||||
|
|
||||||
|
#ifdef GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB
|
||||||
|
if (arb_frag_program && glGetProgramivARB)
|
||||||
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,
|
||||||
|
GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &max_native_instr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (arb_npot && arb_frag_program &&
|
||||||
|
(max_texture_size >= 8192) && (max_native_instr >= 4096))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_SRGB_FBO_ES3:
|
||||||
|
#ifdef HAVE_OPENGLES
|
||||||
|
if (major >= 3)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case GL_CAPS_SRGB_FBO:
|
||||||
|
#if defined(HAVE_OPENGLES)
|
||||||
|
if (major >= 3 || gl_query_extension("EXT_sRGB"))
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
if (gl_check_capability(GL_CAPS_FBO))
|
||||||
|
{
|
||||||
|
if ( gl_query_core_context_in_use() ||
|
||||||
|
(gl_query_extension("EXT_texture_sRGB")
|
||||||
|
&& gl_query_extension("ARB_framebuffer_sRGB"))
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GL_CAPS_FP_FBO:
|
||||||
|
/* GLES - No extensions for float FBO currently. */
|
||||||
|
#ifndef HAVE_OPENGLES
|
||||||
|
if (gl_check_capability(GL_CAPS_FBO))
|
||||||
|
{
|
||||||
|
/* Float FBO is core in 3.2. */
|
||||||
|
if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float"))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_BGRA8888:
|
||||||
|
#ifdef HAVE_OPENGLES
|
||||||
|
/* There are both APPLE and EXT variants. */
|
||||||
|
if (gl_query_extension("BGRA8888") && !strstr(renderer, "VideoCore"))
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_TEX_STORAGE:
|
||||||
|
#ifdef HAVE_OPENGLES
|
||||||
|
if (major >= 3)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
if (strstr(vendor, "ATI Technologies"))
|
||||||
|
return false;
|
||||||
|
if (gl_query_extension("ARB_texture_storage"))
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_TEX_STORAGE_EXT:
|
||||||
|
#ifdef TARGET_OS_IPHONE
|
||||||
|
/* Not working on iOS */
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
if (gl_query_extension("EXT_texture_storage"))
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case GL_CAPS_NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,817 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (pixconv.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <retro_inline.h>
|
||||||
|
|
||||||
|
#include <gfx/scaler/pixconv.h>
|
||||||
|
|
||||||
|
#ifdef SCALER_NO_SIMD
|
||||||
|
#undef __SSE2__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void conv_rgb565_0rgb1555(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
const uint16_t *input = (const uint16_t*)input_;
|
||||||
|
uint16_t *output = (uint16_t*)output_;
|
||||||
|
|
||||||
|
#if defined(__SSE2_)
|
||||||
|
int max_width = width - 7;
|
||||||
|
const __m128i hi_mask = _mm_set1_epi16(0x7fe0);
|
||||||
|
const __m128i lo_mask = _mm_set1_epi16(0x1f);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 1, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
int w = 0;
|
||||||
|
#if defined(__SSE2_)
|
||||||
|
for (; w < max_width; w += 8)
|
||||||
|
{
|
||||||
|
const __m128i in = _mm_loadu_si128((const __m128i*)(input + w));
|
||||||
|
__m128i hi = _mm_and_si128(_mm_slli_epi16(in, 1), hi_mask);
|
||||||
|
__m128i lo = _mm_and_si128(in, lo_mask);
|
||||||
|
_mm_storeu_si128((__m128i*)(output + w), _mm_or_si128(hi, lo));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; w < width; w++)
|
||||||
|
{
|
||||||
|
uint16_t col = input[w];
|
||||||
|
uint16_t hi = (col >> 1) & 0x7fe0;
|
||||||
|
uint16_t lo = col & 0x1f;
|
||||||
|
output[w] = hi | lo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_0rgb1555_rgb565(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
const uint16_t *input = (const uint16_t*)input_;
|
||||||
|
uint16_t *output = (uint16_t*)output_;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
int max_width = width - 7;
|
||||||
|
|
||||||
|
const __m128i hi_mask = _mm_set1_epi16(
|
||||||
|
(int16_t)((0x1f << 11) | (0x1f << 6)));
|
||||||
|
const __m128i lo_mask = _mm_set1_epi16(0x1f);
|
||||||
|
const __m128i glow_mask = _mm_set1_epi16(1 << 5);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 1, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
int w = 0;
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
for (; w < max_width; w += 8)
|
||||||
|
{
|
||||||
|
const __m128i in = _mm_loadu_si128((const __m128i*)(input + w));
|
||||||
|
__m128i rg = _mm_and_si128(_mm_slli_epi16(in, 1), hi_mask);
|
||||||
|
__m128i b = _mm_and_si128(in, lo_mask);
|
||||||
|
__m128i glow = _mm_and_si128(_mm_srli_epi16(in, 4), glow_mask);
|
||||||
|
_mm_storeu_si128((__m128i*)(output + w),
|
||||||
|
_mm_or_si128(rg, _mm_or_si128(b, glow)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; w < width; w++)
|
||||||
|
{
|
||||||
|
uint16_t col = input[w];
|
||||||
|
uint16_t rg = (col << 1) & ((0x1f << 11) | (0x1f << 6));
|
||||||
|
uint16_t b = col & 0x1f;
|
||||||
|
uint16_t glow = (col >> 4) & (1 << 5);
|
||||||
|
output[w] = rg | b | glow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_0rgb1555_argb8888(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
const uint16_t *input = (const uint16_t*)input_;
|
||||||
|
uint32_t *output = (uint32_t*)output_;
|
||||||
|
|
||||||
|
#ifdef __SSE2__
|
||||||
|
const __m128i pix_mask_r = _mm_set1_epi16(0x1f << 10);
|
||||||
|
const __m128i pix_mask_gb = _mm_set1_epi16(0x1f << 5);
|
||||||
|
const __m128i mul15_mid = _mm_set1_epi16(0x4200);
|
||||||
|
const __m128i mul15_hi = _mm_set1_epi16(0x0210);
|
||||||
|
const __m128i a = _mm_set1_epi16(0x00ff);
|
||||||
|
|
||||||
|
int max_width = width - 7;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 2, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
int w = 0;
|
||||||
|
#ifdef __SSE2__
|
||||||
|
for (; w < max_width; w += 8)
|
||||||
|
{
|
||||||
|
__m128i res_lo_bg, res_hi_bg;
|
||||||
|
__m128i res_lo_ra, res_hi_ra;
|
||||||
|
__m128i res_lo, res_hi;
|
||||||
|
const __m128i in = _mm_loadu_si128((const __m128i*)(input + w));
|
||||||
|
__m128i r = _mm_and_si128(in, pix_mask_r);
|
||||||
|
__m128i g = _mm_and_si128(in, pix_mask_gb);
|
||||||
|
__m128i b = _mm_and_si128(_mm_slli_epi16(in, 5), pix_mask_gb);
|
||||||
|
|
||||||
|
r = _mm_mulhi_epi16(r, mul15_hi);
|
||||||
|
g = _mm_mulhi_epi16(g, mul15_mid);
|
||||||
|
b = _mm_mulhi_epi16(b, mul15_mid);
|
||||||
|
|
||||||
|
res_lo_bg = _mm_unpacklo_epi8(b, g);
|
||||||
|
res_hi_bg = _mm_unpackhi_epi8(b, g);
|
||||||
|
res_lo_ra = _mm_unpacklo_epi8(r, a);
|
||||||
|
res_hi_ra = _mm_unpackhi_epi8(r, a);
|
||||||
|
|
||||||
|
res_lo = _mm_or_si128(res_lo_bg,
|
||||||
|
_mm_slli_si128(res_lo_ra, 2));
|
||||||
|
res_hi = _mm_or_si128(res_hi_bg,
|
||||||
|
_mm_slli_si128(res_hi_ra, 2));
|
||||||
|
|
||||||
|
_mm_storeu_si128((__m128i*)(output + w + 0), res_lo);
|
||||||
|
_mm_storeu_si128((__m128i*)(output + w + 4), res_hi);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
uint32_t r = (col >> 10) & 0x1f;
|
||||||
|
uint32_t g = (col >> 5) & 0x1f;
|
||||||
|
uint32_t b = (col >> 0) & 0x1f;
|
||||||
|
r = (r << 3) | (r >> 2);
|
||||||
|
g = (g << 3) | (g >> 2);
|
||||||
|
b = (b << 3) | (b >> 2);
|
||||||
|
|
||||||
|
output[w] = (0xffu << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_rgb565_argb8888(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
const uint16_t *input = (const uint16_t*)input_;
|
||||||
|
uint32_t *output = (uint32_t*)output_;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
const __m128i pix_mask_r = _mm_set1_epi16(0x1f << 10);
|
||||||
|
const __m128i pix_mask_g = _mm_set1_epi16(0x3f << 5);
|
||||||
|
const __m128i pix_mask_b = _mm_set1_epi16(0x1f << 5);
|
||||||
|
const __m128i mul16_r = _mm_set1_epi16(0x0210);
|
||||||
|
const __m128i mul16_g = _mm_set1_epi16(0x2080);
|
||||||
|
const __m128i mul16_b = _mm_set1_epi16(0x4200);
|
||||||
|
const __m128i a = _mm_set1_epi16(0x00ff);
|
||||||
|
|
||||||
|
int max_width = width - 7;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 2, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
int w = 0;
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
for (; w < max_width; w += 8)
|
||||||
|
{
|
||||||
|
__m128i res_lo, res_hi;
|
||||||
|
__m128i res_lo_bg, res_hi_bg, res_lo_ra, res_hi_ra;
|
||||||
|
const __m128i in = _mm_loadu_si128((const __m128i*)(input + w));
|
||||||
|
__m128i r = _mm_and_si128(_mm_srli_epi16(in, 1), pix_mask_r);
|
||||||
|
__m128i g = _mm_and_si128(in, pix_mask_g);
|
||||||
|
__m128i b = _mm_and_si128(_mm_slli_epi16(in, 5), pix_mask_b);
|
||||||
|
|
||||||
|
r = _mm_mulhi_epi16(r, mul16_r);
|
||||||
|
g = _mm_mulhi_epi16(g, mul16_g);
|
||||||
|
b = _mm_mulhi_epi16(b, mul16_b);
|
||||||
|
|
||||||
|
res_lo_bg = _mm_unpacklo_epi8(b, g);
|
||||||
|
res_hi_bg = _mm_unpackhi_epi8(b, g);
|
||||||
|
res_lo_ra = _mm_unpacklo_epi8(r, a);
|
||||||
|
res_hi_ra = _mm_unpackhi_epi8(r, a);
|
||||||
|
|
||||||
|
res_lo = _mm_or_si128(res_lo_bg,
|
||||||
|
_mm_slli_si128(res_lo_ra, 2));
|
||||||
|
res_hi = _mm_or_si128(res_hi_bg,
|
||||||
|
_mm_slli_si128(res_hi_ra, 2));
|
||||||
|
|
||||||
|
_mm_storeu_si128((__m128i*)(output + w + 0), res_lo);
|
||||||
|
_mm_storeu_si128((__m128i*)(output + w + 4), res_hi);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
uint32_t r = (col >> 11) & 0x1f;
|
||||||
|
uint32_t g = (col >> 5) & 0x3f;
|
||||||
|
uint32_t b = (col >> 0) & 0x1f;
|
||||||
|
r = (r << 3) | (r >> 2);
|
||||||
|
g = (g << 2) | (g >> 4);
|
||||||
|
b = (b << 3) | (b >> 2);
|
||||||
|
|
||||||
|
output[w] = (0xffu << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_argb8888_rgba4444(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h, w;
|
||||||
|
const uint32_t *input = (const uint32_t*)input_;
|
||||||
|
uint16_t *output = (uint16_t*)output_;
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 2, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
for (w = 0; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
uint32_t r = (col >> 16) & 0xf;
|
||||||
|
uint32_t g = (col >> 8) & 0xf;
|
||||||
|
uint32_t b = (col) & 0xf;
|
||||||
|
uint32_t a = (col >> 24) & 0xf;
|
||||||
|
r = (r >> 4) | r;
|
||||||
|
g = (g >> 4) | g;
|
||||||
|
b = (b >> 4) | b;
|
||||||
|
a = (a >> 4) | a;
|
||||||
|
|
||||||
|
output[w] = (r << 12) | (g << 8) | (b << 4) | a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_rgba4444_argb8888(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h, w;
|
||||||
|
const uint16_t *input = (const uint16_t*)input_;
|
||||||
|
uint32_t *output = (uint32_t*)output_;
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 2, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
for (w = 0; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
uint32_t r = (col >> 12) & 0xf;
|
||||||
|
uint32_t g = (col >> 8) & 0xf;
|
||||||
|
uint32_t b = (col >> 4) & 0xf;
|
||||||
|
uint32_t a = (col >> 0) & 0xf;
|
||||||
|
r = (r << 4) | r;
|
||||||
|
g = (g << 4) | g;
|
||||||
|
b = (b << 4) | b;
|
||||||
|
a = (a << 4) | a;
|
||||||
|
|
||||||
|
output[w] = (a << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_rgba4444_rgb565(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h, w;
|
||||||
|
const uint16_t *input = (const uint16_t*)input_;
|
||||||
|
uint16_t *output = (uint16_t*)output_;
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 1, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
for (w = 0; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
uint32_t r = (col >> 12) & 0xf;
|
||||||
|
uint32_t g = (col >> 8) & 0xf;
|
||||||
|
uint32_t b = (col >> 4) & 0xf;
|
||||||
|
|
||||||
|
output[w] = (r << 12) | (g << 7) | (b << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
/* :( TODO: Make this saner. */
|
||||||
|
static INLINE void store_bgr24_sse2(void *output, __m128i a,
|
||||||
|
__m128i b, __m128i c, __m128i d)
|
||||||
|
{
|
||||||
|
const __m128i mask_0 = _mm_set_epi32(0, 0, 0, 0x00ffffff);
|
||||||
|
const __m128i mask_1 = _mm_set_epi32(0, 0, 0x00ffffff, 0);
|
||||||
|
const __m128i mask_2 = _mm_set_epi32(0, 0x00ffffff, 0, 0);
|
||||||
|
const __m128i mask_3 = _mm_set_epi32(0x00ffffff, 0, 0, 0);
|
||||||
|
|
||||||
|
__m128i a0 = _mm_and_si128(a, mask_0);
|
||||||
|
__m128i a1 = _mm_srli_si128(_mm_and_si128(a, mask_1), 1);
|
||||||
|
__m128i a2 = _mm_srli_si128(_mm_and_si128(a, mask_2), 2);
|
||||||
|
__m128i a3 = _mm_srli_si128(_mm_and_si128(a, mask_3), 3);
|
||||||
|
__m128i a4 = _mm_slli_si128(_mm_and_si128(b, mask_0), 12);
|
||||||
|
__m128i a5 = _mm_slli_si128(_mm_and_si128(b, mask_1), 11);
|
||||||
|
|
||||||
|
__m128i b0 = _mm_srli_si128(_mm_and_si128(b, mask_1), 5);
|
||||||
|
__m128i b1 = _mm_srli_si128(_mm_and_si128(b, mask_2), 6);
|
||||||
|
__m128i b2 = _mm_srli_si128(_mm_and_si128(b, mask_3), 7);
|
||||||
|
__m128i b3 = _mm_slli_si128(_mm_and_si128(c, mask_0), 8);
|
||||||
|
__m128i b4 = _mm_slli_si128(_mm_and_si128(c, mask_1), 7);
|
||||||
|
__m128i b5 = _mm_slli_si128(_mm_and_si128(c, mask_2), 6);
|
||||||
|
|
||||||
|
__m128i c0 = _mm_srli_si128(_mm_and_si128(c, mask_2), 10);
|
||||||
|
__m128i c1 = _mm_srli_si128(_mm_and_si128(c, mask_3), 11);
|
||||||
|
__m128i c2 = _mm_slli_si128(_mm_and_si128(d, mask_0), 4);
|
||||||
|
__m128i c3 = _mm_slli_si128(_mm_and_si128(d, mask_1), 3);
|
||||||
|
__m128i c4 = _mm_slli_si128(_mm_and_si128(d, mask_2), 2);
|
||||||
|
__m128i c5 = _mm_slli_si128(_mm_and_si128(d, mask_3), 1);
|
||||||
|
|
||||||
|
__m128i *out = (__m128i*)output;
|
||||||
|
|
||||||
|
_mm_storeu_si128(out + 0,
|
||||||
|
_mm_or_si128(a0, _mm_or_si128(a1, _mm_or_si128(a2,
|
||||||
|
_mm_or_si128(a3, _mm_or_si128(a4, a5))))));
|
||||||
|
|
||||||
|
_mm_storeu_si128(out + 1,
|
||||||
|
_mm_or_si128(b0, _mm_or_si128(b1, _mm_or_si128(b2,
|
||||||
|
_mm_or_si128(b3, _mm_or_si128(b4, b5))))));
|
||||||
|
|
||||||
|
_mm_storeu_si128(out + 2,
|
||||||
|
_mm_or_si128(c0, _mm_or_si128(c1, _mm_or_si128(c2,
|
||||||
|
_mm_or_si128(c3, _mm_or_si128(c4, c5))))));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void conv_0rgb1555_bgr24(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
const uint16_t *input = (const uint16_t*)input_;
|
||||||
|
uint8_t *output = (uint8_t*)output_;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
const __m128i pix_mask_r = _mm_set1_epi16(0x1f << 10);
|
||||||
|
const __m128i pix_mask_gb = _mm_set1_epi16(0x1f << 5);
|
||||||
|
const __m128i mul15_mid = _mm_set1_epi16(0x4200);
|
||||||
|
const __m128i mul15_hi = _mm_set1_epi16(0x0210);
|
||||||
|
const __m128i a = _mm_set1_epi16(0x00ff);
|
||||||
|
|
||||||
|
int max_width = width - 15;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
uint8_t *out = output;
|
||||||
|
int w = 0;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
for (; w < max_width; w += 16, out += 48)
|
||||||
|
{
|
||||||
|
__m128i res_lo_bg0, res_lo_bg1, res_hi_bg0, res_hi_bg1,
|
||||||
|
res_lo_ra0, res_lo_ra1, res_hi_ra0, res_hi_ra1,
|
||||||
|
res_lo0, res_lo1, res_hi0, res_hi1;
|
||||||
|
const __m128i in0 = _mm_loadu_si128((const __m128i*)(input + w + 0));
|
||||||
|
const __m128i in1 = _mm_loadu_si128((const __m128i*)(input + w + 8));
|
||||||
|
__m128i r0 = _mm_and_si128(in0, pix_mask_r);
|
||||||
|
__m128i r1 = _mm_and_si128(in1, pix_mask_r);
|
||||||
|
__m128i g0 = _mm_and_si128(in0, pix_mask_gb);
|
||||||
|
__m128i g1 = _mm_and_si128(in1, pix_mask_gb);
|
||||||
|
__m128i b0 = _mm_and_si128(_mm_slli_epi16(in0, 5), pix_mask_gb);
|
||||||
|
__m128i b1 = _mm_and_si128(_mm_slli_epi16(in1, 5), pix_mask_gb);
|
||||||
|
|
||||||
|
r0 = _mm_mulhi_epi16(r0, mul15_hi);
|
||||||
|
r1 = _mm_mulhi_epi16(r1, mul15_hi);
|
||||||
|
g0 = _mm_mulhi_epi16(g0, mul15_mid);
|
||||||
|
g1 = _mm_mulhi_epi16(g1, mul15_mid);
|
||||||
|
b0 = _mm_mulhi_epi16(b0, mul15_mid);
|
||||||
|
b1 = _mm_mulhi_epi16(b1, mul15_mid);
|
||||||
|
|
||||||
|
res_lo_bg0 = _mm_unpacklo_epi8(b0, g0);
|
||||||
|
res_lo_bg1 = _mm_unpacklo_epi8(b1, g1);
|
||||||
|
res_hi_bg0 = _mm_unpackhi_epi8(b0, g0);
|
||||||
|
res_hi_bg1 = _mm_unpackhi_epi8(b1, g1);
|
||||||
|
res_lo_ra0 = _mm_unpacklo_epi8(r0, a);
|
||||||
|
res_lo_ra1 = _mm_unpacklo_epi8(r1, a);
|
||||||
|
res_hi_ra0 = _mm_unpackhi_epi8(r0, a);
|
||||||
|
res_hi_ra1 = _mm_unpackhi_epi8(r1, a);
|
||||||
|
|
||||||
|
res_lo0 = _mm_or_si128(res_lo_bg0,
|
||||||
|
_mm_slli_si128(res_lo_ra0, 2));
|
||||||
|
res_lo1 = _mm_or_si128(res_lo_bg1,
|
||||||
|
_mm_slli_si128(res_lo_ra1, 2));
|
||||||
|
res_hi0 = _mm_or_si128(res_hi_bg0,
|
||||||
|
_mm_slli_si128(res_hi_ra0, 2));
|
||||||
|
res_hi1 = _mm_or_si128(res_hi_bg1,
|
||||||
|
_mm_slli_si128(res_hi_ra1, 2));
|
||||||
|
|
||||||
|
/* Non-POT pixel sizes for the loss */
|
||||||
|
store_bgr24_sse2(out, res_lo0, res_hi0, res_lo1, res_hi1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
uint32_t b = (col >> 0) & 0x1f;
|
||||||
|
uint32_t g = (col >> 5) & 0x1f;
|
||||||
|
uint32_t r = (col >> 10) & 0x1f;
|
||||||
|
b = (b << 3) | (b >> 2);
|
||||||
|
g = (g << 3) | (g >> 2);
|
||||||
|
r = (r << 3) | (r >> 2);
|
||||||
|
|
||||||
|
*out++ = b;
|
||||||
|
*out++ = g;
|
||||||
|
*out++ = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_rgb565_bgr24(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
const uint16_t *input = (const uint16_t*)input_;
|
||||||
|
uint8_t *output = (uint8_t*)output_;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
const __m128i pix_mask_r = _mm_set1_epi16(0x1f << 10);
|
||||||
|
const __m128i pix_mask_g = _mm_set1_epi16(0x3f << 5);
|
||||||
|
const __m128i pix_mask_b = _mm_set1_epi16(0x1f << 5);
|
||||||
|
const __m128i mul16_r = _mm_set1_epi16(0x0210);
|
||||||
|
const __m128i mul16_g = _mm_set1_epi16(0x2080);
|
||||||
|
const __m128i mul16_b = _mm_set1_epi16(0x4200);
|
||||||
|
const __m128i a = _mm_set1_epi16(0x00ff);
|
||||||
|
|
||||||
|
int max_width = width - 15;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (h = 0; h < height; h++, output += out_stride, input += in_stride >> 1)
|
||||||
|
{
|
||||||
|
uint8_t *out = output;
|
||||||
|
int w = 0;
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
for (; w < max_width; w += 16, out += 48)
|
||||||
|
{
|
||||||
|
__m128i res_lo_bg0, res_hi_bg0, res_lo_ra0, res_hi_ra0;
|
||||||
|
__m128i res_lo_bg1, res_hi_bg1, res_lo_ra1, res_hi_ra1;
|
||||||
|
__m128i res_lo0, res_hi0, res_lo1, res_hi1;
|
||||||
|
const __m128i in0 = _mm_loadu_si128((const __m128i*)(input + w));
|
||||||
|
const __m128i in1 = _mm_loadu_si128((const __m128i*)(input + w + 8));
|
||||||
|
__m128i r0 = _mm_and_si128(_mm_srli_epi16(in0, 1), pix_mask_r);
|
||||||
|
__m128i g0 = _mm_and_si128(in0, pix_mask_g);
|
||||||
|
__m128i b0 = _mm_and_si128(_mm_slli_epi16(in0, 5), pix_mask_b);
|
||||||
|
__m128i r1 = _mm_and_si128(_mm_srli_epi16(in1, 1), pix_mask_r);
|
||||||
|
__m128i g1 = _mm_and_si128(in1, pix_mask_g);
|
||||||
|
__m128i b1 = _mm_and_si128(_mm_slli_epi16(in1, 5), pix_mask_b);
|
||||||
|
|
||||||
|
r0 = _mm_mulhi_epi16(r0, mul16_r);
|
||||||
|
g0 = _mm_mulhi_epi16(g0, mul16_g);
|
||||||
|
b0 = _mm_mulhi_epi16(b0, mul16_b);
|
||||||
|
r1 = _mm_mulhi_epi16(r1, mul16_r);
|
||||||
|
g1 = _mm_mulhi_epi16(g1, mul16_g);
|
||||||
|
b1 = _mm_mulhi_epi16(b1, mul16_b);
|
||||||
|
|
||||||
|
res_lo_bg0 = _mm_unpacklo_epi8(b0, g0);
|
||||||
|
res_hi_bg0 = _mm_unpackhi_epi8(b0, g0);
|
||||||
|
res_lo_ra0 = _mm_unpacklo_epi8(r0, a);
|
||||||
|
res_hi_ra0 = _mm_unpackhi_epi8(r0, a);
|
||||||
|
res_lo_bg1 = _mm_unpacklo_epi8(b1, g1);
|
||||||
|
res_hi_bg1 = _mm_unpackhi_epi8(b1, g1);
|
||||||
|
res_lo_ra1 = _mm_unpacklo_epi8(r1, a);
|
||||||
|
res_hi_ra1 = _mm_unpackhi_epi8(r1, a);
|
||||||
|
|
||||||
|
res_lo0 = _mm_or_si128(res_lo_bg0,
|
||||||
|
_mm_slli_si128(res_lo_ra0, 2));
|
||||||
|
res_hi0 = _mm_or_si128(res_hi_bg0,
|
||||||
|
_mm_slli_si128(res_hi_ra0, 2));
|
||||||
|
res_lo1 = _mm_or_si128(res_lo_bg1,
|
||||||
|
_mm_slli_si128(res_lo_ra1, 2));
|
||||||
|
res_hi1 = _mm_or_si128(res_hi_bg1,
|
||||||
|
_mm_slli_si128(res_hi_ra1, 2));
|
||||||
|
|
||||||
|
store_bgr24_sse2(out, res_lo0, res_hi0, res_lo1, res_hi1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
uint32_t r = (col >> 11) & 0x1f;
|
||||||
|
uint32_t g = (col >> 5) & 0x3f;
|
||||||
|
uint32_t b = (col >> 0) & 0x1f;
|
||||||
|
r = (r << 3) | (r >> 2);
|
||||||
|
g = (g << 2) | (g >> 4);
|
||||||
|
b = (b << 3) | (b >> 2);
|
||||||
|
|
||||||
|
*out++ = b;
|
||||||
|
*out++ = g;
|
||||||
|
*out++ = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_bgr24_argb8888(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h, w;
|
||||||
|
const uint8_t *input = (const uint8_t*)input_;
|
||||||
|
uint32_t *output = (uint32_t*)output_;
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 2, input += in_stride)
|
||||||
|
{
|
||||||
|
const uint8_t *inp = input;
|
||||||
|
for (w = 0; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t b = *inp++;
|
||||||
|
uint32_t g = *inp++;
|
||||||
|
uint32_t r = *inp++;
|
||||||
|
output[w] = (0xffu << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_argb8888_0rgb1555(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h, w;
|
||||||
|
const uint32_t *input = (const uint32_t*)input_;
|
||||||
|
uint16_t *output = (uint16_t*)output_;
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 1, input += in_stride >> 2)
|
||||||
|
{
|
||||||
|
for (w = 0; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
uint16_t r = (col >> 19) & 0x1f;
|
||||||
|
uint16_t g = (col >> 11) & 0x1f;
|
||||||
|
uint16_t b = (col >> 3) & 0x1f;
|
||||||
|
output[w] = (r << 10) | (g << 5) | (b << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_argb8888_bgr24(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
const uint32_t *input = (const uint32_t*)input_;
|
||||||
|
uint8_t *output = (uint8_t*)output_;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
int max_width = width - 15;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride, input += in_stride >> 2)
|
||||||
|
{
|
||||||
|
uint8_t *out = output;
|
||||||
|
int w = 0;
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
for (; w < max_width; w += 16, out += 48)
|
||||||
|
{
|
||||||
|
store_bgr24_sse2(out,
|
||||||
|
_mm_loadu_si128((const __m128i*)(input + w + 0)),
|
||||||
|
_mm_loadu_si128((const __m128i*)(input + w + 4)),
|
||||||
|
_mm_loadu_si128((const __m128i*)(input + w + 8)),
|
||||||
|
_mm_loadu_si128((const __m128i*)(input + w + 12)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
*out++ = (uint8_t)(col >> 0);
|
||||||
|
*out++ = (uint8_t)(col >> 8);
|
||||||
|
*out++ = (uint8_t)(col >> 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_argb8888_abgr8888(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h, w;
|
||||||
|
const uint32_t *input = (const uint32_t*)input_;
|
||||||
|
uint32_t *output = (uint32_t*)output_;
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride >> 2, input += in_stride >> 2)
|
||||||
|
{
|
||||||
|
for (w = 0; w < width; w++)
|
||||||
|
{
|
||||||
|
uint32_t col = input[w];
|
||||||
|
output[w] = ((col << 16) & 0xff0000) |
|
||||||
|
((col >> 16) & 0xff) | (col & 0xff00ff00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define YUV_SHIFT 6
|
||||||
|
#define YUV_OFFSET (1 << (YUV_SHIFT - 1))
|
||||||
|
#define YUV_MAT_Y (1 << 6)
|
||||||
|
#define YUV_MAT_U_G (-22)
|
||||||
|
#define YUV_MAT_U_B (113)
|
||||||
|
#define YUV_MAT_V_R (90)
|
||||||
|
#define YUV_MAT_V_G (-46)
|
||||||
|
|
||||||
|
void conv_yuyv_argb8888(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
const uint8_t *input = (const uint8_t*)input_;
|
||||||
|
uint32_t *output = (uint32_t*)output_;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
const __m128i mask_y = _mm_set1_epi16(0xffu);
|
||||||
|
const __m128i mask_u = _mm_set1_epi32(0xffu << 8);
|
||||||
|
const __m128i mask_v = _mm_set1_epi32(0xffu << 24);
|
||||||
|
const __m128i chroma_offset = _mm_set1_epi16(128);
|
||||||
|
const __m128i round_offset = _mm_set1_epi16(YUV_OFFSET);
|
||||||
|
|
||||||
|
const __m128i yuv_mul = _mm_set1_epi16(YUV_MAT_Y);
|
||||||
|
const __m128i u_g_mul = _mm_set1_epi16(YUV_MAT_U_G);
|
||||||
|
const __m128i u_b_mul = _mm_set1_epi16(YUV_MAT_U_B);
|
||||||
|
const __m128i v_r_mul = _mm_set1_epi16(YUV_MAT_V_R);
|
||||||
|
const __m128i v_g_mul = _mm_set1_epi16(YUV_MAT_V_G);
|
||||||
|
const __m128i a = _mm_cmpeq_epi16(
|
||||||
|
_mm_setzero_si128(), _mm_setzero_si128());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (h = 0; h < height; h++, output += out_stride >> 2, input += in_stride)
|
||||||
|
{
|
||||||
|
const uint8_t *src = input;
|
||||||
|
uint32_t *dst = output;
|
||||||
|
int w = 0;
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
/* Each loop processes 16 pixels. */
|
||||||
|
for (; w + 16 <= width; w += 16, src += 32, dst += 16)
|
||||||
|
{
|
||||||
|
__m128i u, v, u0_g, u1_g, u0_b, u1_b, v0_r, v1_r, v0_g, v1_g,
|
||||||
|
r0, g0, b0, r1, g1, b1;
|
||||||
|
__m128i res_lo_bg, res_hi_bg, res_lo_ra, res_hi_ra;
|
||||||
|
__m128i res0, res1, res2, res3;
|
||||||
|
__m128i yuv0 = _mm_loadu_si128((const __m128i*)(src + 0)); /* [Y0, U0, Y1, V0, Y2, U1, Y3, V1, ...] */
|
||||||
|
__m128i yuv1 = _mm_loadu_si128((const __m128i*)(src + 16)); /* [Y0, U0, Y1, V0, Y2, U1, Y3, V1, ...] */
|
||||||
|
|
||||||
|
__m128i _y0 = _mm_and_si128(yuv0, mask_y); /* [Y0, Y1, Y2, ...] (16-bit) */
|
||||||
|
__m128i u0 = _mm_and_si128(yuv0, mask_u); /* [0, U0, 0, 0, 0, U1, 0, 0, ...] */
|
||||||
|
__m128i v0 = _mm_and_si128(yuv0, mask_v); /* [0, 0, 0, V1, 0, , 0, V1, ...] */
|
||||||
|
__m128i _y1 = _mm_and_si128(yuv1, mask_y); /* [Y0, Y1, Y2, ...] (16-bit) */
|
||||||
|
__m128i u1 = _mm_and_si128(yuv1, mask_u); /* [0, U0, 0, 0, 0, U1, 0, 0, ...] */
|
||||||
|
__m128i v1 = _mm_and_si128(yuv1, mask_v); /* [0, 0, 0, V1, 0, , 0, V1, ...] */
|
||||||
|
|
||||||
|
/* Juggle around to get U and V in the same 16-bit format as Y. */
|
||||||
|
u0 = _mm_srli_si128(u0, 1);
|
||||||
|
v0 = _mm_srli_si128(v0, 3);
|
||||||
|
u1 = _mm_srli_si128(u1, 1);
|
||||||
|
v1 = _mm_srli_si128(v1, 3);
|
||||||
|
u = _mm_packs_epi32(u0, u1);
|
||||||
|
v = _mm_packs_epi32(v0, v1);
|
||||||
|
|
||||||
|
/* Apply YUV offsets (U, V) -= (-128, -128). */
|
||||||
|
u = _mm_sub_epi16(u, chroma_offset);
|
||||||
|
v = _mm_sub_epi16(v, chroma_offset);
|
||||||
|
|
||||||
|
/* Upscale chroma horizontally (nearest). */
|
||||||
|
u0 = _mm_unpacklo_epi16(u, u);
|
||||||
|
u1 = _mm_unpackhi_epi16(u, u);
|
||||||
|
v0 = _mm_unpacklo_epi16(v, v);
|
||||||
|
v1 = _mm_unpackhi_epi16(v, v);
|
||||||
|
|
||||||
|
/* Apply transformations. */
|
||||||
|
_y0 = _mm_mullo_epi16(_y0, yuv_mul);
|
||||||
|
_y1 = _mm_mullo_epi16(_y1, yuv_mul);
|
||||||
|
u0_g = _mm_mullo_epi16(u0, u_g_mul);
|
||||||
|
u1_g = _mm_mullo_epi16(u1, u_g_mul);
|
||||||
|
u0_b = _mm_mullo_epi16(u0, u_b_mul);
|
||||||
|
u1_b = _mm_mullo_epi16(u1, u_b_mul);
|
||||||
|
v0_r = _mm_mullo_epi16(v0, v_r_mul);
|
||||||
|
v1_r = _mm_mullo_epi16(v1, v_r_mul);
|
||||||
|
v0_g = _mm_mullo_epi16(v0, v_g_mul);
|
||||||
|
v1_g = _mm_mullo_epi16(v1, v_g_mul);
|
||||||
|
|
||||||
|
/* Add contibutions from the transformed components. */
|
||||||
|
r0 = _mm_srai_epi16(_mm_adds_epi16(_mm_adds_epi16(_y0, v0_r),
|
||||||
|
round_offset), YUV_SHIFT);
|
||||||
|
g0 = _mm_srai_epi16(_mm_adds_epi16(
|
||||||
|
_mm_adds_epi16(_mm_adds_epi16(_y0, v0_g), u0_g), round_offset), YUV_SHIFT);
|
||||||
|
b0 = _mm_srai_epi16(_mm_adds_epi16(
|
||||||
|
_mm_adds_epi16(_y0, u0_b), round_offset), YUV_SHIFT);
|
||||||
|
|
||||||
|
r1 = _mm_srai_epi16(_mm_adds_epi16(
|
||||||
|
_mm_adds_epi16(_y1, v1_r), round_offset), YUV_SHIFT);
|
||||||
|
g1 = _mm_srai_epi16(_mm_adds_epi16(
|
||||||
|
_mm_adds_epi16(_mm_adds_epi16(_y1, v1_g), u1_g), round_offset), YUV_SHIFT);
|
||||||
|
b1 = _mm_srai_epi16(_mm_adds_epi16(
|
||||||
|
_mm_adds_epi16(_y1, u1_b), round_offset), YUV_SHIFT);
|
||||||
|
|
||||||
|
/* Saturate into 8-bit. */
|
||||||
|
r0 = _mm_packus_epi16(r0, r1);
|
||||||
|
g0 = _mm_packus_epi16(g0, g1);
|
||||||
|
b0 = _mm_packus_epi16(b0, b1);
|
||||||
|
|
||||||
|
/* Interleave into ARGB. */
|
||||||
|
res_lo_bg = _mm_unpacklo_epi8(b0, g0);
|
||||||
|
res_hi_bg = _mm_unpackhi_epi8(b0, g0);
|
||||||
|
res_lo_ra = _mm_unpacklo_epi8(r0, a);
|
||||||
|
res_hi_ra = _mm_unpackhi_epi8(r0, a);
|
||||||
|
res0 = _mm_unpacklo_epi16(res_lo_bg, res_lo_ra);
|
||||||
|
res1 = _mm_unpackhi_epi16(res_lo_bg, res_lo_ra);
|
||||||
|
res2 = _mm_unpacklo_epi16(res_hi_bg, res_hi_ra);
|
||||||
|
res3 = _mm_unpackhi_epi16(res_hi_bg, res_hi_ra);
|
||||||
|
|
||||||
|
_mm_storeu_si128((__m128i*)(dst + 0), res0);
|
||||||
|
_mm_storeu_si128((__m128i*)(dst + 4), res1);
|
||||||
|
_mm_storeu_si128((__m128i*)(dst + 8), res2);
|
||||||
|
_mm_storeu_si128((__m128i*)(dst + 12), res3);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Finish off the rest (if any) in C. */
|
||||||
|
for (; w < width; w += 2, src += 4, dst += 2)
|
||||||
|
{
|
||||||
|
int _y0 = src[0];
|
||||||
|
int u = src[1] - 128;
|
||||||
|
int _y1 = src[2];
|
||||||
|
int v = src[3] - 128;
|
||||||
|
|
||||||
|
uint8_t r0 = clamp_8bit((YUV_MAT_Y * _y0 + YUV_MAT_V_R * v + YUV_OFFSET) >> YUV_SHIFT);
|
||||||
|
uint8_t g0 = clamp_8bit((YUV_MAT_Y * _y0 + YUV_MAT_U_G * u + YUV_MAT_V_G * v + YUV_OFFSET) >> YUV_SHIFT);
|
||||||
|
uint8_t b0 = clamp_8bit((YUV_MAT_Y * _y0 + YUV_MAT_U_B * u + YUV_OFFSET) >> YUV_SHIFT);
|
||||||
|
|
||||||
|
uint8_t r1 = clamp_8bit((YUV_MAT_Y * _y1 + YUV_MAT_V_R * v + YUV_OFFSET) >> YUV_SHIFT);
|
||||||
|
uint8_t g1 = clamp_8bit((YUV_MAT_Y * _y1 + YUV_MAT_U_G * u + YUV_MAT_V_G * v + YUV_OFFSET) >> YUV_SHIFT);
|
||||||
|
uint8_t b1 = clamp_8bit((YUV_MAT_Y * _y1 + YUV_MAT_U_B * u + YUV_OFFSET) >> YUV_SHIFT);
|
||||||
|
|
||||||
|
dst[0] = 0xff000000u | (r0 << 16) | (g0 << 8) | (b0 << 0);
|
||||||
|
dst[1] = 0xff000000u | (r1 << 16) | (g1 << 8) | (b1 << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conv_copy(void *output_, const void *input_,
|
||||||
|
int width, int height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
int copy_len = abs(out_stride);
|
||||||
|
const uint8_t *input = (const uint8_t*)input_;
|
||||||
|
uint8_t *output = (uint8_t*)output_;
|
||||||
|
|
||||||
|
if (abs(in_stride) < copy_len)
|
||||||
|
copy_len = abs(in_stride);
|
||||||
|
|
||||||
|
for (h = 0; h < height;
|
||||||
|
h++, output += out_stride, input += in_stride)
|
||||||
|
memcpy(output, input, copy_len);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,343 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (scaler.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
|
||||||
|
#include <gfx/scaler/scaler.h>
|
||||||
|
#include <gfx/scaler/scaler_int.h>
|
||||||
|
#include <gfx/scaler/filter.h>
|
||||||
|
#include <gfx/scaler/pixconv.h>
|
||||||
|
|
||||||
|
static bool allocate_frames(struct scaler_ctx *ctx)
|
||||||
|
{
|
||||||
|
uint64_t *scaled_frame = NULL;
|
||||||
|
ctx->scaled.stride = ((ctx->out_width + 7) & ~7) * sizeof(uint64_t);
|
||||||
|
ctx->scaled.width = ctx->out_width;
|
||||||
|
ctx->scaled.height = ctx->in_height;
|
||||||
|
scaled_frame = (uint64_t*)calloc(sizeof(uint64_t),
|
||||||
|
(ctx->scaled.stride * ctx->scaled.height) >> 3);
|
||||||
|
|
||||||
|
if (!scaled_frame)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ctx->scaled.frame = scaled_frame;
|
||||||
|
|
||||||
|
if (ctx->in_fmt != SCALER_FMT_ARGB8888)
|
||||||
|
{
|
||||||
|
uint32_t *input_frame = NULL;
|
||||||
|
ctx->input.stride = ((ctx->in_width + 7) & ~7) * sizeof(uint32_t);
|
||||||
|
input_frame = (uint32_t*)calloc(sizeof(uint32_t),
|
||||||
|
(ctx->input.stride * ctx->in_height) >> 2);
|
||||||
|
|
||||||
|
if (!input_frame)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ctx->input.frame = input_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->out_fmt != SCALER_FMT_ARGB8888)
|
||||||
|
{
|
||||||
|
uint32_t *output_frame = NULL;
|
||||||
|
ctx->output.stride = ((ctx->out_width + 7) & ~7) * sizeof(uint32_t);
|
||||||
|
|
||||||
|
output_frame = (uint32_t*)calloc(sizeof(uint32_t),
|
||||||
|
(ctx->output.stride * ctx->out_height) >> 2);
|
||||||
|
|
||||||
|
if (!output_frame)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ctx->output.frame = output_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scaler_ctx_gen_filter(struct scaler_ctx *ctx)
|
||||||
|
{
|
||||||
|
scaler_ctx_gen_reset(ctx);
|
||||||
|
|
||||||
|
ctx->scaler_special = NULL;
|
||||||
|
ctx->unscaled = false;
|
||||||
|
|
||||||
|
if (!allocate_frames(ctx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( ctx->in_width == ctx->out_width
|
||||||
|
&& ctx->in_height == ctx->out_height)
|
||||||
|
{
|
||||||
|
ctx->unscaled = true; /* Only pixel format conversion ... */
|
||||||
|
|
||||||
|
if (ctx->in_fmt == ctx->out_fmt)
|
||||||
|
ctx->direct_pixconv = conv_copy;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Bind a pixel converter callback function to the
|
||||||
|
* 'direct_pixconv' function pointer of the scaler context object. */
|
||||||
|
switch (ctx->in_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_0RGB1555:
|
||||||
|
switch (ctx->out_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_ARGB8888:
|
||||||
|
ctx->direct_pixconv = conv_0rgb1555_argb8888;
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_RGB565:
|
||||||
|
ctx->direct_pixconv = conv_0rgb1555_rgb565;
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_BGR24:
|
||||||
|
ctx->direct_pixconv = conv_0rgb1555_bgr24;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_RGB565:
|
||||||
|
switch (ctx->out_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_ARGB8888:
|
||||||
|
ctx->direct_pixconv = conv_rgb565_argb8888;
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_BGR24:
|
||||||
|
ctx->direct_pixconv = conv_rgb565_bgr24;
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_0RGB1555:
|
||||||
|
ctx->direct_pixconv = conv_rgb565_0rgb1555;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_BGR24:
|
||||||
|
switch (ctx->out_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_ARGB8888:
|
||||||
|
ctx->direct_pixconv = conv_bgr24_argb8888;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_ARGB8888:
|
||||||
|
switch (ctx->out_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_0RGB1555:
|
||||||
|
ctx->direct_pixconv = conv_argb8888_0rgb1555;
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_BGR24:
|
||||||
|
ctx->direct_pixconv = conv_argb8888_bgr24;
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_ABGR8888:
|
||||||
|
ctx->direct_pixconv = conv_argb8888_abgr8888;
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_RGBA4444:
|
||||||
|
ctx->direct_pixconv = conv_argb8888_rgba4444;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_YUYV:
|
||||||
|
switch (ctx->out_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_ARGB8888:
|
||||||
|
ctx->direct_pixconv = conv_yuyv_argb8888;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_RGBA4444:
|
||||||
|
switch (ctx->out_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_ARGB8888:
|
||||||
|
ctx->direct_pixconv = conv_rgba4444_argb8888;
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_RGB565:
|
||||||
|
ctx->direct_pixconv = conv_rgba4444_rgb565;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCALER_FMT_ABGR8888:
|
||||||
|
/* FIXME/TODO */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->direct_pixconv)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ctx->scaler_horiz = scaler_argb8888_horiz;
|
||||||
|
ctx->scaler_vert = scaler_argb8888_vert;
|
||||||
|
|
||||||
|
switch (ctx->in_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_ARGB8888:
|
||||||
|
/* No need to convert :D */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_FMT_0RGB1555:
|
||||||
|
ctx->in_pixconv = conv_0rgb1555_argb8888;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_FMT_RGB565:
|
||||||
|
ctx->in_pixconv = conv_rgb565_argb8888;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_FMT_BGR24:
|
||||||
|
ctx->in_pixconv = conv_bgr24_argb8888;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_FMT_RGBA4444:
|
||||||
|
ctx->in_pixconv = conv_rgba4444_argb8888;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ctx->out_fmt)
|
||||||
|
{
|
||||||
|
case SCALER_FMT_ARGB8888:
|
||||||
|
/* No need to convert :D */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_FMT_RGBA4444:
|
||||||
|
ctx->out_pixconv = conv_argb8888_rgba4444;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_FMT_0RGB1555:
|
||||||
|
ctx->out_pixconv = conv_argb8888_0rgb1555;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_FMT_BGR24:
|
||||||
|
ctx->out_pixconv = conv_argb8888_bgr24;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scaler_gen_filter(ctx))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scaler_ctx_gen_reset(struct scaler_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->horiz.filter)
|
||||||
|
free(ctx->horiz.filter);
|
||||||
|
if (ctx->horiz.filter_pos)
|
||||||
|
free(ctx->horiz.filter_pos);
|
||||||
|
if (ctx->vert.filter)
|
||||||
|
free(ctx->vert.filter);
|
||||||
|
if (ctx->vert.filter_pos)
|
||||||
|
free(ctx->vert.filter_pos);
|
||||||
|
if (ctx->scaled.frame)
|
||||||
|
free(ctx->scaled.frame);
|
||||||
|
if (ctx->input.frame)
|
||||||
|
free(ctx->input.frame);
|
||||||
|
if (ctx->output.frame)
|
||||||
|
free(ctx->output.frame);
|
||||||
|
|
||||||
|
ctx->horiz.filter = NULL;
|
||||||
|
ctx->horiz.filter_len = 0;
|
||||||
|
ctx->horiz.filter_stride = 0;
|
||||||
|
ctx->horiz.filter_pos = NULL;
|
||||||
|
|
||||||
|
ctx->vert.filter = NULL;
|
||||||
|
ctx->vert.filter_len = 0;
|
||||||
|
ctx->vert.filter_stride = 0;
|
||||||
|
ctx->vert.filter_pos = NULL;
|
||||||
|
|
||||||
|
ctx->scaled.frame = NULL;
|
||||||
|
ctx->scaled.width = 0;
|
||||||
|
ctx->scaled.height = 0;
|
||||||
|
ctx->scaled.stride = 0;
|
||||||
|
|
||||||
|
ctx->input.frame = NULL;
|
||||||
|
ctx->input.stride = 0;
|
||||||
|
|
||||||
|
ctx->output.frame = NULL;
|
||||||
|
ctx->output.stride = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scaler_ctx_scale:
|
||||||
|
* @ctx : pointer to scaler context object.
|
||||||
|
* @output : pointer to output image.
|
||||||
|
* @input : pointer to input image.
|
||||||
|
*
|
||||||
|
* Scales an input image to an output image.
|
||||||
|
**/
|
||||||
|
void scaler_ctx_scale(struct scaler_ctx *ctx,
|
||||||
|
void *output, const void *input)
|
||||||
|
{
|
||||||
|
const void *input_frame = input;
|
||||||
|
void *output_frame = output;
|
||||||
|
int input_stride = ctx->in_stride;
|
||||||
|
int output_stride = ctx->out_stride;
|
||||||
|
|
||||||
|
if (ctx->in_fmt != SCALER_FMT_ARGB8888)
|
||||||
|
{
|
||||||
|
ctx->in_pixconv(ctx->input.frame, input,
|
||||||
|
ctx->in_width, ctx->in_height,
|
||||||
|
ctx->input.stride, ctx->in_stride);
|
||||||
|
|
||||||
|
input_frame = ctx->input.frame;
|
||||||
|
input_stride = ctx->input.stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->out_fmt != SCALER_FMT_ARGB8888)
|
||||||
|
{
|
||||||
|
output_frame = ctx->output.frame;
|
||||||
|
output_stride = ctx->output.stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take some special, and (hopefully) more optimized path. */
|
||||||
|
if (ctx->scaler_special)
|
||||||
|
ctx->scaler_special(ctx, output_frame, input_frame,
|
||||||
|
ctx->out_width, ctx->out_height,
|
||||||
|
ctx->in_width, ctx->in_height,
|
||||||
|
output_stride, input_stride);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Take generic filter path. */
|
||||||
|
if (ctx->scaler_horiz)
|
||||||
|
ctx->scaler_horiz(ctx, input_frame, input_stride);
|
||||||
|
if (ctx->scaler_vert)
|
||||||
|
ctx->scaler_vert (ctx, output, output_stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->out_fmt != SCALER_FMT_ARGB8888)
|
||||||
|
ctx->out_pixconv(output, ctx->output.frame,
|
||||||
|
ctx->out_width, ctx->out_height,
|
||||||
|
ctx->out_stride, ctx->output.stride);
|
||||||
|
}
|
|
@ -0,0 +1,243 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (scaler_filter.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
|
||||||
|
#include <gfx/scaler/filter.h>
|
||||||
|
#include <gfx/scaler/scaler_int.h>
|
||||||
|
#include <retro_inline.h>
|
||||||
|
#include <filters.h>
|
||||||
|
#include <retro_math.h>
|
||||||
|
|
||||||
|
#define FILTER_UNITY (1 << 14)
|
||||||
|
|
||||||
|
static INLINE void gen_filter_point_sub(struct scaler_filter *filter,
|
||||||
|
int len, int pos, int step)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++, pos += step)
|
||||||
|
{
|
||||||
|
filter->filter_pos[i] = pos >> 16;
|
||||||
|
filter->filter[i] = FILTER_UNITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void gen_filter_bilinear_sub(struct scaler_filter *filter,
|
||||||
|
int len, int pos, int step)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++, pos += step)
|
||||||
|
{
|
||||||
|
filter->filter_pos[i] = pos >> 16;
|
||||||
|
filter->filter[i * 2 + 1] = (pos & 0xffff) >> 2;
|
||||||
|
filter->filter[i * 2 + 0] = FILTER_UNITY - filter->filter[i * 2 + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void gen_filter_sinc_sub(struct scaler_filter *filter,
|
||||||
|
int len, int pos, int step, double phase_mul)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
const int sinc_size = filter->filter_len;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++, pos += step)
|
||||||
|
{
|
||||||
|
filter->filter_pos[i] = pos >> 16;
|
||||||
|
|
||||||
|
for (j = 0; j < sinc_size; j++)
|
||||||
|
{
|
||||||
|
double sinc_phase = M_PI * ((double)((sinc_size << 15) + (pos & 0xffff)) / 0x10000 - j);
|
||||||
|
double lanczos_phase = sinc_phase / ((sinc_size >> 1));
|
||||||
|
int16_t sinc_val = FILTER_UNITY * sinc(sinc_phase * phase_mul) * sinc(lanczos_phase) * phase_mul;
|
||||||
|
|
||||||
|
filter->filter[i * sinc_size + j] = sinc_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool validate_filter(struct scaler_ctx *ctx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int max_h_pos;
|
||||||
|
int max_w_pos = ctx->in_width - ctx->horiz.filter_len;
|
||||||
|
|
||||||
|
for (i = 0; i < ctx->out_width; i++)
|
||||||
|
{
|
||||||
|
if (ctx->horiz.filter_pos[i] > max_w_pos || ctx->horiz.filter_pos[i] < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Out X = %d => In X = %d\n", i, ctx->horiz.filter_pos[i]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
max_h_pos = ctx->in_height - ctx->vert.filter_len;
|
||||||
|
|
||||||
|
for (i = 0; i < ctx->out_height; i++)
|
||||||
|
{
|
||||||
|
if (ctx->vert.filter_pos[i] > max_h_pos || ctx->vert.filter_pos[i] < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Out Y = %d => In Y = %d\n", i, ctx->vert.filter_pos[i]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fixup_filter_sub(struct scaler_filter *filter,
|
||||||
|
int out_len, int in_len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int max_pos = in_len - filter->filter_len;
|
||||||
|
|
||||||
|
for (i = 0; i < out_len; i++)
|
||||||
|
{
|
||||||
|
int postsample = filter->filter_pos[i] - max_pos;
|
||||||
|
int presample = -filter->filter_pos[i];
|
||||||
|
|
||||||
|
if (postsample > 0)
|
||||||
|
{
|
||||||
|
int16_t *base_filter = NULL;
|
||||||
|
|
||||||
|
filter->filter_pos[i] -= postsample;
|
||||||
|
|
||||||
|
base_filter = filter->filter + i * filter->filter_stride;
|
||||||
|
|
||||||
|
if (postsample > (int)filter->filter_len)
|
||||||
|
memset(base_filter, 0, filter->filter_len * sizeof(int16_t));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memmove(base_filter + postsample, base_filter,
|
||||||
|
(filter->filter_len - postsample) * sizeof(int16_t));
|
||||||
|
memset(base_filter, 0, postsample * sizeof(int16_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presample > 0)
|
||||||
|
{
|
||||||
|
int16_t *base_filter = NULL;
|
||||||
|
|
||||||
|
filter->filter_pos[i] += presample;
|
||||||
|
base_filter = filter->filter + i * filter->filter_stride;
|
||||||
|
|
||||||
|
if (presample > (int)filter->filter_len)
|
||||||
|
memset(base_filter, 0, filter->filter_len * sizeof(int16_t));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memmove(base_filter, base_filter + presample,
|
||||||
|
(filter->filter_len - presample) * sizeof(int16_t));
|
||||||
|
memset(base_filter + (filter->filter_len - presample),
|
||||||
|
0, presample * sizeof(int16_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scaler_gen_filter(struct scaler_ctx *ctx)
|
||||||
|
{
|
||||||
|
int x_pos, x_step, y_pos, y_step;
|
||||||
|
int sinc_size = 0;
|
||||||
|
|
||||||
|
switch (ctx->scaler_type)
|
||||||
|
{
|
||||||
|
case SCALER_TYPE_POINT:
|
||||||
|
ctx->horiz.filter_len = 1;
|
||||||
|
ctx->horiz.filter_stride = 1;
|
||||||
|
ctx->vert.filter_len = 1;
|
||||||
|
ctx->vert.filter_stride = 1;
|
||||||
|
break;
|
||||||
|
case SCALER_TYPE_BILINEAR:
|
||||||
|
ctx->horiz.filter_len = 2;
|
||||||
|
ctx->horiz.filter_stride = 2;
|
||||||
|
ctx->vert.filter_len = 2;
|
||||||
|
ctx->vert.filter_stride = 2;
|
||||||
|
break;
|
||||||
|
case SCALER_TYPE_SINC:
|
||||||
|
sinc_size = 8 * ((ctx->in_width > ctx->out_width)
|
||||||
|
? next_pow2(ctx->in_width / ctx->out_width) : 1);
|
||||||
|
ctx->horiz.filter_len = sinc_size;
|
||||||
|
ctx->horiz.filter_stride = sinc_size;
|
||||||
|
ctx->vert.filter_len = sinc_size;
|
||||||
|
ctx->vert.filter_stride = sinc_size;
|
||||||
|
break;
|
||||||
|
case SCALER_TYPE_UNKNOWN:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->horiz.filter = (int16_t*)calloc(sizeof(int16_t), ctx->horiz.filter_stride * ctx->out_width);
|
||||||
|
ctx->horiz.filter_pos = (int*)calloc(sizeof(int), ctx->out_width);
|
||||||
|
|
||||||
|
ctx->vert.filter = (int16_t*)calloc(sizeof(int16_t), ctx->vert.filter_stride * ctx->out_height);
|
||||||
|
ctx->vert.filter_pos = (int*)calloc(sizeof(int), ctx->out_height);
|
||||||
|
|
||||||
|
if (!ctx->horiz.filter || !ctx->vert.filter)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
x_step = (1 << 16) * ctx->in_width / ctx->out_width;
|
||||||
|
y_step = (1 << 16) * ctx->in_height / ctx->out_height;
|
||||||
|
|
||||||
|
switch (ctx->scaler_type)
|
||||||
|
{
|
||||||
|
case SCALER_TYPE_POINT:
|
||||||
|
x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15);
|
||||||
|
y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15);
|
||||||
|
|
||||||
|
gen_filter_point_sub(&ctx->horiz, ctx->out_width, x_pos, x_step);
|
||||||
|
gen_filter_point_sub(&ctx->vert, ctx->out_height, y_pos, y_step);
|
||||||
|
|
||||||
|
ctx->scaler_special = scaler_argb8888_point_special;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_TYPE_BILINEAR:
|
||||||
|
x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15);
|
||||||
|
y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15);
|
||||||
|
|
||||||
|
gen_filter_bilinear_sub(&ctx->horiz, ctx->out_width, x_pos, x_step);
|
||||||
|
gen_filter_bilinear_sub(&ctx->vert, ctx->out_height, y_pos, y_step);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCALER_TYPE_SINC:
|
||||||
|
/* Need to expand the filter when downsampling
|
||||||
|
* to get a proper low-pass effect. */
|
||||||
|
|
||||||
|
x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15) - (sinc_size << 15);
|
||||||
|
y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15) - (sinc_size << 15);
|
||||||
|
|
||||||
|
gen_filter_sinc_sub(&ctx->horiz, ctx->out_width, x_pos, x_step,
|
||||||
|
ctx->in_width > ctx->out_width ? (double)ctx->out_width / ctx->in_width : 1.0);
|
||||||
|
gen_filter_sinc_sub(&ctx->vert, ctx->out_height, y_pos, y_step,
|
||||||
|
ctx->in_height > ctx->out_height ? (double)ctx->out_height / ctx->in_height : 1.0
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case SCALER_TYPE_UNKNOWN:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Makes sure that we never sample outside our rectangle. */
|
||||||
|
fixup_filter_sub(&ctx->horiz, ctx->out_width, ctx->in_width);
|
||||||
|
fixup_filter_sub(&ctx->vert, ctx->out_height, ctx->in_height);
|
||||||
|
|
||||||
|
return validate_filter(ctx);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,261 @@
|
||||||
|
/* Copyright (C) 2010-2017 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (scaler_int.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 <gfx/scaler/scaler_int.h>
|
||||||
|
|
||||||
|
#include <retro_inline.h>
|
||||||
|
|
||||||
|
#ifdef SCALER_NO_SIMD
|
||||||
|
#undef __SSE2__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ARGB8888 scaler is split in two:
|
||||||
|
*
|
||||||
|
* First, horizontal scaler is applied.
|
||||||
|
* Here, all 8-bit channels are expanded to 16-bit. Values are then shifted 7
|
||||||
|
* to left to occupy 15 bits.
|
||||||
|
*
|
||||||
|
* The sign bit is kept empty as we have to do signed multiplication for the
|
||||||
|
* filter.
|
||||||
|
*
|
||||||
|
* A mulhi [(a * b) >> 16] is applied which loses some precision, but is
|
||||||
|
* very efficient for SIMD.
|
||||||
|
* It is accurate enough for 8-bit purposes.
|
||||||
|
*
|
||||||
|
* The fixed point 1.0 for filter is (1 << 14). After horizontal scale,
|
||||||
|
* the output is kept with 16-bit channels, and will now have 13 bits
|
||||||
|
* of precision as [(a * (1 << 14)) >> 16] is effectively a right shift by 2.
|
||||||
|
*
|
||||||
|
* Vertical scaler takes the 13 bit channels, and performs the
|
||||||
|
* same mulhi steps.
|
||||||
|
* Another 2 bits of precision is lost, which ends up as 11 bits.
|
||||||
|
* Scaling is now complete. Channels are shifted right by 3, and saturated
|
||||||
|
* into 8-bit values.
|
||||||
|
*
|
||||||
|
* The C version of scalers perform the exact same operations as the
|
||||||
|
* SIMD code for testing purposes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void scaler_argb8888_vert(const struct scaler_ctx *ctx, void *output_, int stride)
|
||||||
|
{
|
||||||
|
int h, w, y;
|
||||||
|
const uint64_t *input = ctx->scaled.frame;
|
||||||
|
uint32_t *output = (uint32_t*)output_;
|
||||||
|
|
||||||
|
const int16_t *filter_vert = ctx->vert.filter;
|
||||||
|
|
||||||
|
for (h = 0; h < ctx->out_height; h++,
|
||||||
|
filter_vert += ctx->vert.filter_stride, output += stride >> 2)
|
||||||
|
{
|
||||||
|
const uint64_t *input_base = input + ctx->vert.filter_pos[h]
|
||||||
|
* (ctx->scaled.stride >> 3);
|
||||||
|
|
||||||
|
for (w = 0; w < ctx->out_width; w++)
|
||||||
|
{
|
||||||
|
const uint64_t *input_base_y = input_base + w;
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
__m128i final;
|
||||||
|
__m128i res = _mm_setzero_si128();
|
||||||
|
|
||||||
|
for (y = 0; (y + 1) < ctx->vert.filter_len; y += 2,
|
||||||
|
input_base_y += (ctx->scaled.stride >> 2))
|
||||||
|
{
|
||||||
|
__m128i coeff = _mm_set_epi64x(filter_vert[y + 1] * 0x0001000100010001ll, filter_vert[y + 0] * 0x0001000100010001ll);
|
||||||
|
__m128i col = _mm_set_epi64x(input_base_y[ctx->scaled.stride >> 3], input_base_y[0]);
|
||||||
|
|
||||||
|
res = _mm_adds_epi16(_mm_mulhi_epi16(col, coeff), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; y < ctx->vert.filter_len; y++, input_base_y += (ctx->scaled.stride >> 3))
|
||||||
|
{
|
||||||
|
__m128i coeff = _mm_set_epi64x(0, filter_vert[y] * 0x0001000100010001ll);
|
||||||
|
__m128i col = _mm_set_epi64x(0, input_base_y[0]);
|
||||||
|
|
||||||
|
res = _mm_adds_epi16(_mm_mulhi_epi16(col, coeff), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = _mm_adds_epi16(_mm_srli_si128(res, 8), res);
|
||||||
|
res = _mm_srai_epi16(res, (7 - 2 - 2));
|
||||||
|
|
||||||
|
final = _mm_packus_epi16(res, res);
|
||||||
|
|
||||||
|
output[w] = _mm_cvtsi128_si32(final);
|
||||||
|
#else
|
||||||
|
int16_t res_a = 0;
|
||||||
|
int16_t res_r = 0;
|
||||||
|
int16_t res_g = 0;
|
||||||
|
int16_t res_b = 0;
|
||||||
|
|
||||||
|
for (y = 0; y < ctx->vert.filter_len; y++,
|
||||||
|
input_base_y += (ctx->scaled.stride >> 3))
|
||||||
|
{
|
||||||
|
uint64_t col = *input_base_y;
|
||||||
|
|
||||||
|
int16_t a = (col >> 48) & 0xffff;
|
||||||
|
int16_t r = (col >> 32) & 0xffff;
|
||||||
|
int16_t g = (col >> 16) & 0xffff;
|
||||||
|
int16_t b = (col >> 0) & 0xffff;
|
||||||
|
|
||||||
|
int16_t coeff = filter_vert[y];
|
||||||
|
|
||||||
|
res_a += (a * coeff) >> 16;
|
||||||
|
res_r += (r * coeff) >> 16;
|
||||||
|
res_g += (g * coeff) >> 16;
|
||||||
|
res_b += (b * coeff) >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
res_a >>= (7 - 2 - 2);
|
||||||
|
res_r >>= (7 - 2 - 2);
|
||||||
|
res_g >>= (7 - 2 - 2);
|
||||||
|
res_b >>= (7 - 2 - 2);
|
||||||
|
|
||||||
|
output[w] =
|
||||||
|
(clamp_8bit(res_a) << 24) |
|
||||||
|
(clamp_8bit(res_r) << 16) |
|
||||||
|
(clamp_8bit(res_g) << 8) |
|
||||||
|
(clamp_8bit(res_b) << 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scaler_argb8888_horiz(const struct scaler_ctx *ctx, const void *input_, int stride)
|
||||||
|
{
|
||||||
|
int h, w, x;
|
||||||
|
const uint32_t *input = (uint32_t*)input_;
|
||||||
|
uint64_t *output = ctx->scaled.frame;
|
||||||
|
|
||||||
|
for (h = 0; h < ctx->scaled.height; h++, input += stride >> 2,
|
||||||
|
output += ctx->scaled.stride >> 3)
|
||||||
|
{
|
||||||
|
const int16_t *filter_horiz = ctx->horiz.filter;
|
||||||
|
|
||||||
|
for (w = 0; w < ctx->scaled.width; w++,
|
||||||
|
filter_horiz += ctx->horiz.filter_stride)
|
||||||
|
{
|
||||||
|
const uint32_t *input_base_x = input + ctx->horiz.filter_pos[w];
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
__m128i res = _mm_setzero_si128();
|
||||||
|
|
||||||
|
for (x = 0; (x + 1) < ctx->horiz.filter_len; x += 2)
|
||||||
|
{
|
||||||
|
__m128i coeff = _mm_set_epi64x(filter_horiz[x + 1] * 0x0001000100010001ll, filter_horiz[x + 0] * 0x0001000100010001ll);
|
||||||
|
|
||||||
|
__m128i col = _mm_unpacklo_epi8(_mm_set_epi64x(0,
|
||||||
|
((uint64_t)input_base_x[x + 1] << 32) | input_base_x[x + 0]), _mm_setzero_si128());
|
||||||
|
|
||||||
|
col = _mm_slli_epi16(col, 7);
|
||||||
|
res = _mm_adds_epi16(_mm_mulhi_epi16(col, coeff), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; x < ctx->horiz.filter_len; x++)
|
||||||
|
{
|
||||||
|
__m128i coeff = _mm_set_epi64x(0, filter_horiz[x] * 0x0001000100010001ll);
|
||||||
|
__m128i col = _mm_unpacklo_epi8(_mm_set_epi32(0, 0, 0, input_base_x[x]), _mm_setzero_si128());
|
||||||
|
|
||||||
|
col = _mm_slli_epi16(col, 7);
|
||||||
|
res = _mm_adds_epi16(_mm_mulhi_epi16(col, coeff), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = _mm_adds_epi16(_mm_srli_si128(res, 8), res);
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
output[w] = _mm_cvtsi128_si64(res);
|
||||||
|
#else /* 32-bit doesn't have si64. Do it in two steps. */
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint32_t *u32;
|
||||||
|
uint64_t *u64;
|
||||||
|
} u;
|
||||||
|
u.u64 = output + w;
|
||||||
|
u.u32[0] = _mm_cvtsi128_si32(res);
|
||||||
|
u.u32[1] = _mm_cvtsi128_si32(_mm_srli_si128(res, 4));
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
int16_t res_a = 0;
|
||||||
|
int16_t res_r = 0;
|
||||||
|
int16_t res_g = 0;
|
||||||
|
int16_t res_b = 0;
|
||||||
|
|
||||||
|
for (x = 0; x < ctx->horiz.filter_len; x++)
|
||||||
|
{
|
||||||
|
uint32_t col = input_base_x[x];
|
||||||
|
|
||||||
|
int16_t a = (col >> (24 - 7)) & (0xff << 7);
|
||||||
|
int16_t r = (col >> (16 - 7)) & (0xff << 7);
|
||||||
|
int16_t g = (col >> ( 8 - 7)) & (0xff << 7);
|
||||||
|
int16_t b = (col << ( 0 + 7)) & (0xff << 7);
|
||||||
|
|
||||||
|
int16_t coeff = filter_horiz[x];
|
||||||
|
|
||||||
|
res_a += (a * coeff) >> 16;
|
||||||
|
res_r += (r * coeff) >> 16;
|
||||||
|
res_g += (g * coeff) >> 16;
|
||||||
|
res_b += (b * coeff) >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[w] = (
|
||||||
|
(uint64_t)res_a << 48) |
|
||||||
|
((uint64_t)res_r << 32) |
|
||||||
|
((uint64_t)res_g << 16) |
|
||||||
|
((uint64_t)res_b << 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scaler_argb8888_point_special(const struct scaler_ctx *ctx,
|
||||||
|
void *output_, const void *input_,
|
||||||
|
int out_width, int out_height,
|
||||||
|
int in_width, int in_height,
|
||||||
|
int out_stride, int in_stride)
|
||||||
|
{
|
||||||
|
int h, w;
|
||||||
|
int x_pos = (1 << 15) * in_width / out_width - (1 << 15);
|
||||||
|
int x_step = (1 << 16) * in_width / out_width;
|
||||||
|
int y_pos = (1 << 15) * in_height / out_height - (1 << 15);
|
||||||
|
int y_step = (1 << 16) * in_height / out_height;
|
||||||
|
const uint32_t *input = (const uint32_t*)input_;
|
||||||
|
uint32_t *output = (uint32_t*)output_;
|
||||||
|
|
||||||
|
if (x_pos < 0)
|
||||||
|
x_pos = 0;
|
||||||
|
if (y_pos < 0)
|
||||||
|
y_pos = 0;
|
||||||
|
|
||||||
|
for (h = 0; h < out_height; h++, y_pos += y_step, output += out_stride >> 2)
|
||||||
|
{
|
||||||
|
int x = x_pos;
|
||||||
|
const uint32_t *inp = input + (y_pos >> 16) * (in_stride >> 2);
|
||||||
|
|
||||||
|
for (w = 0; w < out_width; w++, x += x_step)
|
||||||
|
output[w] = inp[x >> 16];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,12 @@
|
||||||
|
# Autogenerate GL extension loaders
|
||||||
|
|
||||||
|
## OpenGL desktop
|
||||||
|
|
||||||
|
Use Khronos' recent [header](www.opengl.org/registry/api/glext.h).
|
||||||
|
|
||||||
|
./glgen.py /usr/include/GL/glext.h glsym_gl.h glsym_gl.c
|
||||||
|
|
||||||
|
## OpenGL ES
|
||||||
|
|
||||||
|
./glgen.py /usr/include/GLES2/gl2ext.h glsym_es2.h glsym_es2.c
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
License statement applies to this file (glgen.py) only.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
banned_ext = [ 'AMD', 'APPLE', 'NV', 'NVX', 'ATI', '3DLABS', 'SUN', 'SGI', 'SGIX', 'SGIS', 'INTEL', '3DFX', 'IBM', 'MESA', 'GREMEDY', 'OML', 'PGI', 'I3D', 'INGL', 'MTX', 'QCOM', 'IMG', 'ANGLE', 'SUNX', 'INGR' ]
|
||||||
|
|
||||||
|
def noext(sym):
|
||||||
|
for ext in banned_ext:
|
||||||
|
if sym.endswith(ext):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def find_gl_symbols(lines):
|
||||||
|
typedefs = []
|
||||||
|
syms = []
|
||||||
|
for line in lines:
|
||||||
|
m = re.search(r'^typedef.+PFN(\S+)PROC.+$', line)
|
||||||
|
g = re.search(r'^.+(gl\S+)\W*\(.+\).*$', line)
|
||||||
|
if m and noext(m.group(1)):
|
||||||
|
typedefs.append(m.group(0).replace('PFN', 'RGLSYM').replace('GLDEBUGPROC', 'RGLGENGLDEBUGPROC'))
|
||||||
|
if g and noext(g.group(1)):
|
||||||
|
syms.append(g.group(1))
|
||||||
|
return (typedefs, syms)
|
||||||
|
|
||||||
|
def generate_defines(gl_syms):
|
||||||
|
res = []
|
||||||
|
for line in gl_syms:
|
||||||
|
res.append('#define {} __rglgen_{}'.format(line, line))
|
||||||
|
return res
|
||||||
|
|
||||||
|
def generate_declarations(gl_syms):
|
||||||
|
return ['RGLSYM' + x.upper() + 'PROC ' + '__rglgen_' + x + ';' for x in gl_syms]
|
||||||
|
|
||||||
|
def generate_macros(gl_syms):
|
||||||
|
return [' SYM(' + x.replace('gl', '') + '),' for x in gl_syms]
|
||||||
|
|
||||||
|
def dump(f, lines):
|
||||||
|
f.write('\n'.join(lines))
|
||||||
|
f.write('\n\n')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
if len(sys.argv) > 4:
|
||||||
|
for banned in sys.argv[4:]:
|
||||||
|
banned_ext.append(banned)
|
||||||
|
|
||||||
|
with open(sys.argv[1], 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
typedefs, syms = find_gl_symbols(lines)
|
||||||
|
|
||||||
|
overrides = generate_defines(syms)
|
||||||
|
declarations = generate_declarations(syms)
|
||||||
|
externs = ['extern ' + x for x in declarations]
|
||||||
|
|
||||||
|
macros = generate_macros(syms)
|
||||||
|
|
||||||
|
with open(sys.argv[2], 'w') as f:
|
||||||
|
f.write('#ifndef RGLGEN_DECL_H__\n')
|
||||||
|
f.write('#define RGLGEN_DECL_H__\n')
|
||||||
|
|
||||||
|
f.write('#ifdef __cplusplus\n')
|
||||||
|
f.write('extern "C" {\n')
|
||||||
|
f.write('#endif\n')
|
||||||
|
|
||||||
|
f.write('#ifdef GL_APIENTRY\n')
|
||||||
|
f.write('typedef void (GL_APIENTRY *RGLGENGLDEBUGPROC)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*, GLvoid*);\n')
|
||||||
|
f.write('typedef void (GL_APIENTRY *RGLGENGLDEBUGPROCKHR)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*, GLvoid*);\n')
|
||||||
|
f.write('#else\n')
|
||||||
|
f.write('#ifndef APIENTRY\n')
|
||||||
|
f.write('#define APIENTRY\n')
|
||||||
|
f.write('#endif\n')
|
||||||
|
f.write('#ifndef APIENTRYP\n')
|
||||||
|
f.write('#define APIENTRYP APIENTRY *\n')
|
||||||
|
f.write('#endif\n')
|
||||||
|
f.write('typedef void (APIENTRY *RGLGENGLDEBUGPROCARB)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*, GLvoid*);\n')
|
||||||
|
f.write('typedef void (APIENTRY *RGLGENGLDEBUGPROC)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*, GLvoid*);\n')
|
||||||
|
f.write('#endif\n')
|
||||||
|
|
||||||
|
f.write('#ifndef GL_OES_EGL_image\n')
|
||||||
|
f.write('typedef void *GLeglImageOES;\n')
|
||||||
|
f.write('#endif\n')
|
||||||
|
|
||||||
|
f.write('#if !defined(GL_OES_fixed_point) && !defined(HAVE_OPENGLES2)\n')
|
||||||
|
f.write('typedef GLint GLfixed;\n')
|
||||||
|
f.write('#endif\n')
|
||||||
|
|
||||||
|
dump(f, typedefs)
|
||||||
|
dump(f, overrides)
|
||||||
|
dump(f, externs)
|
||||||
|
|
||||||
|
f.write('struct rglgen_sym_map { const char *sym; void *ptr; };\n')
|
||||||
|
f.write('extern const struct rglgen_sym_map rglgen_symbol_map[];\n')
|
||||||
|
|
||||||
|
f.write('#ifdef __cplusplus\n')
|
||||||
|
f.write('}\n')
|
||||||
|
f.write('#endif\n')
|
||||||
|
|
||||||
|
f.write('#endif\n')
|
||||||
|
|
||||||
|
with open(sys.argv[3], 'w') as f:
|
||||||
|
f.write('#include "glsym/glsym.h"\n')
|
||||||
|
f.write('#include <stddef.h>\n')
|
||||||
|
f.write('#define SYM(x) { "gl" #x, &(gl##x) }\n')
|
||||||
|
f.write('const struct rglgen_sym_map rglgen_symbol_map[] = {\n')
|
||||||
|
dump(f, macros)
|
||||||
|
f.write(' { NULL, NULL },\n')
|
||||||
|
f.write('};\n')
|
||||||
|
dump(f, declarations)
|
||||||
|
|
|
@ -0,0 +1,410 @@
|
||||||
|
#include "glsym/glsym.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#define SYM(x) { "gl" #x, &(gl##x) }
|
||||||
|
const struct rglgen_sym_map rglgen_symbol_map[] = {
|
||||||
|
SYM(BlendBarrierKHR),
|
||||||
|
SYM(DebugMessageControlKHR),
|
||||||
|
SYM(DebugMessageInsertKHR),
|
||||||
|
SYM(DebugMessageCallbackKHR),
|
||||||
|
SYM(GetDebugMessageLogKHR),
|
||||||
|
SYM(PushDebugGroupKHR),
|
||||||
|
SYM(PopDebugGroupKHR),
|
||||||
|
SYM(ObjectLabelKHR),
|
||||||
|
SYM(GetObjectLabelKHR),
|
||||||
|
SYM(ObjectPtrLabelKHR),
|
||||||
|
SYM(GetObjectPtrLabelKHR),
|
||||||
|
SYM(GetPointervKHR),
|
||||||
|
SYM(GetGraphicsResetStatusKHR),
|
||||||
|
SYM(ReadnPixelsKHR),
|
||||||
|
SYM(GetnUniformfvKHR),
|
||||||
|
SYM(GetnUniformivKHR),
|
||||||
|
SYM(GetnUniformuivKHR),
|
||||||
|
SYM(EGLImageTargetTexture2DOES),
|
||||||
|
SYM(EGLImageTargetRenderbufferStorageOES),
|
||||||
|
SYM(CopyImageSubDataOES),
|
||||||
|
SYM(EnableiOES),
|
||||||
|
SYM(DisableiOES),
|
||||||
|
SYM(BlendEquationiOES),
|
||||||
|
SYM(BlendEquationSeparateiOES),
|
||||||
|
SYM(BlendFunciOES),
|
||||||
|
SYM(BlendFuncSeparateiOES),
|
||||||
|
SYM(ColorMaskiOES),
|
||||||
|
SYM(IsEnablediOES),
|
||||||
|
SYM(DrawElementsBaseVertexOES),
|
||||||
|
SYM(DrawRangeElementsBaseVertexOES),
|
||||||
|
SYM(DrawElementsInstancedBaseVertexOES),
|
||||||
|
SYM(MultiDrawElementsBaseVertexOES),
|
||||||
|
SYM(FramebufferTextureOES),
|
||||||
|
SYM(GetProgramBinaryOES),
|
||||||
|
SYM(ProgramBinaryOES),
|
||||||
|
SYM(MapBufferOES),
|
||||||
|
SYM(UnmapBufferOES),
|
||||||
|
SYM(GetBufferPointervOES),
|
||||||
|
SYM(PrimitiveBoundingBoxOES),
|
||||||
|
SYM(MinSampleShadingOES),
|
||||||
|
SYM(PatchParameteriOES),
|
||||||
|
SYM(TexImage3DOES),
|
||||||
|
SYM(TexSubImage3DOES),
|
||||||
|
SYM(CopyTexSubImage3DOES),
|
||||||
|
SYM(CompressedTexImage3DOES),
|
||||||
|
SYM(CompressedTexSubImage3DOES),
|
||||||
|
SYM(FramebufferTexture3DOES),
|
||||||
|
SYM(TexParameterIivOES),
|
||||||
|
SYM(TexParameterIuivOES),
|
||||||
|
SYM(GetTexParameterIivOES),
|
||||||
|
SYM(GetTexParameterIuivOES),
|
||||||
|
SYM(SamplerParameterIivOES),
|
||||||
|
SYM(SamplerParameterIuivOES),
|
||||||
|
SYM(GetSamplerParameterIivOES),
|
||||||
|
SYM(GetSamplerParameterIuivOES),
|
||||||
|
SYM(TexBufferOES),
|
||||||
|
SYM(TexBufferRangeOES),
|
||||||
|
SYM(TexStorage3DMultisampleOES),
|
||||||
|
SYM(TextureViewOES),
|
||||||
|
SYM(BindVertexArrayOES),
|
||||||
|
SYM(DeleteVertexArraysOES),
|
||||||
|
SYM(GenVertexArraysOES),
|
||||||
|
SYM(IsVertexArrayOES),
|
||||||
|
SYM(ViewportArrayvOES),
|
||||||
|
SYM(ViewportIndexedfOES),
|
||||||
|
SYM(ViewportIndexedfvOES),
|
||||||
|
SYM(ScissorArrayvOES),
|
||||||
|
SYM(ScissorIndexedOES),
|
||||||
|
SYM(ScissorIndexedvOES),
|
||||||
|
SYM(DepthRangeArrayfvOES),
|
||||||
|
SYM(DepthRangeIndexedfOES),
|
||||||
|
SYM(GetFloati_vOES),
|
||||||
|
SYM(DrawArraysInstancedBaseInstanceEXT),
|
||||||
|
SYM(DrawElementsInstancedBaseInstanceEXT),
|
||||||
|
SYM(DrawElementsInstancedBaseVertexBaseInstanceEXT),
|
||||||
|
SYM(BindFragDataLocationIndexedEXT),
|
||||||
|
SYM(BindFragDataLocationEXT),
|
||||||
|
SYM(GetProgramResourceLocationIndexEXT),
|
||||||
|
SYM(GetFragDataIndexEXT),
|
||||||
|
SYM(BufferStorageEXT),
|
||||||
|
SYM(ClearTexImageEXT),
|
||||||
|
SYM(ClearTexSubImageEXT),
|
||||||
|
SYM(CopyImageSubDataEXT),
|
||||||
|
SYM(LabelObjectEXT),
|
||||||
|
SYM(GetObjectLabelEXT),
|
||||||
|
SYM(InsertEventMarkerEXT),
|
||||||
|
SYM(PushGroupMarkerEXT),
|
||||||
|
SYM(PopGroupMarkerEXT),
|
||||||
|
SYM(DiscardFramebufferEXT),
|
||||||
|
SYM(GenQueriesEXT),
|
||||||
|
SYM(DeleteQueriesEXT),
|
||||||
|
SYM(IsQueryEXT),
|
||||||
|
SYM(BeginQueryEXT),
|
||||||
|
SYM(EndQueryEXT),
|
||||||
|
SYM(QueryCounterEXT),
|
||||||
|
SYM(GetQueryivEXT),
|
||||||
|
SYM(GetQueryObjectivEXT),
|
||||||
|
SYM(GetQueryObjectuivEXT),
|
||||||
|
SYM(DrawBuffersEXT),
|
||||||
|
SYM(EnableiEXT),
|
||||||
|
SYM(DisableiEXT),
|
||||||
|
SYM(BlendEquationiEXT),
|
||||||
|
SYM(BlendEquationSeparateiEXT),
|
||||||
|
SYM(BlendFunciEXT),
|
||||||
|
SYM(BlendFuncSeparateiEXT),
|
||||||
|
SYM(ColorMaskiEXT),
|
||||||
|
SYM(IsEnablediEXT),
|
||||||
|
SYM(DrawElementsBaseVertexEXT),
|
||||||
|
SYM(DrawRangeElementsBaseVertexEXT),
|
||||||
|
SYM(DrawElementsInstancedBaseVertexEXT),
|
||||||
|
SYM(MultiDrawElementsBaseVertexEXT),
|
||||||
|
SYM(DrawArraysInstancedEXT),
|
||||||
|
SYM(DrawElementsInstancedEXT),
|
||||||
|
SYM(FramebufferTextureEXT),
|
||||||
|
SYM(VertexAttribDivisorEXT),
|
||||||
|
SYM(MapBufferRangeEXT),
|
||||||
|
SYM(FlushMappedBufferRangeEXT),
|
||||||
|
SYM(MultiDrawArraysEXT),
|
||||||
|
SYM(MultiDrawElementsEXT),
|
||||||
|
SYM(MultiDrawArraysIndirectEXT),
|
||||||
|
SYM(MultiDrawElementsIndirectEXT),
|
||||||
|
SYM(RenderbufferStorageMultisampleEXT),
|
||||||
|
SYM(FramebufferTexture2DMultisampleEXT),
|
||||||
|
SYM(ReadBufferIndexedEXT),
|
||||||
|
SYM(DrawBuffersIndexedEXT),
|
||||||
|
SYM(GetIntegeri_vEXT),
|
||||||
|
SYM(PolygonOffsetClampEXT),
|
||||||
|
SYM(PrimitiveBoundingBoxEXT),
|
||||||
|
SYM(RasterSamplesEXT),
|
||||||
|
SYM(GetGraphicsResetStatusEXT),
|
||||||
|
SYM(ReadnPixelsEXT),
|
||||||
|
SYM(GetnUniformfvEXT),
|
||||||
|
SYM(GetnUniformivEXT),
|
||||||
|
SYM(ActiveShaderProgramEXT),
|
||||||
|
SYM(BindProgramPipelineEXT),
|
||||||
|
SYM(CreateShaderProgramvEXT),
|
||||||
|
SYM(DeleteProgramPipelinesEXT),
|
||||||
|
SYM(GenProgramPipelinesEXT),
|
||||||
|
SYM(GetProgramPipelineInfoLogEXT),
|
||||||
|
SYM(GetProgramPipelineivEXT),
|
||||||
|
SYM(IsProgramPipelineEXT),
|
||||||
|
SYM(ProgramParameteriEXT),
|
||||||
|
SYM(ProgramUniform1fEXT),
|
||||||
|
SYM(ProgramUniform1fvEXT),
|
||||||
|
SYM(ProgramUniform1iEXT),
|
||||||
|
SYM(ProgramUniform1ivEXT),
|
||||||
|
SYM(ProgramUniform2fEXT),
|
||||||
|
SYM(ProgramUniform2fvEXT),
|
||||||
|
SYM(ProgramUniform2iEXT),
|
||||||
|
SYM(ProgramUniform2ivEXT),
|
||||||
|
SYM(ProgramUniform3fEXT),
|
||||||
|
SYM(ProgramUniform3fvEXT),
|
||||||
|
SYM(ProgramUniform3iEXT),
|
||||||
|
SYM(ProgramUniform3ivEXT),
|
||||||
|
SYM(ProgramUniform4fEXT),
|
||||||
|
SYM(ProgramUniform4fvEXT),
|
||||||
|
SYM(ProgramUniform4iEXT),
|
||||||
|
SYM(ProgramUniform4ivEXT),
|
||||||
|
SYM(ProgramUniformMatrix2fvEXT),
|
||||||
|
SYM(ProgramUniformMatrix3fvEXT),
|
||||||
|
SYM(ProgramUniformMatrix4fvEXT),
|
||||||
|
SYM(UseProgramStagesEXT),
|
||||||
|
SYM(ValidateProgramPipelineEXT),
|
||||||
|
SYM(ProgramUniform1uiEXT),
|
||||||
|
SYM(ProgramUniform2uiEXT),
|
||||||
|
SYM(ProgramUniform3uiEXT),
|
||||||
|
SYM(ProgramUniform4uiEXT),
|
||||||
|
SYM(ProgramUniform1uivEXT),
|
||||||
|
SYM(ProgramUniform2uivEXT),
|
||||||
|
SYM(ProgramUniform3uivEXT),
|
||||||
|
SYM(ProgramUniform4uivEXT),
|
||||||
|
SYM(ProgramUniformMatrix2x3fvEXT),
|
||||||
|
SYM(ProgramUniformMatrix3x2fvEXT),
|
||||||
|
SYM(ProgramUniformMatrix2x4fvEXT),
|
||||||
|
SYM(ProgramUniformMatrix4x2fvEXT),
|
||||||
|
SYM(ProgramUniformMatrix3x4fvEXT),
|
||||||
|
SYM(ProgramUniformMatrix4x3fvEXT),
|
||||||
|
SYM(FramebufferPixelLocalStorageSizeEXT),
|
||||||
|
SYM(GetFramebufferPixelLocalStorageSizeEXT),
|
||||||
|
SYM(ClearPixelLocalStorageuiEXT),
|
||||||
|
SYM(TexPageCommitmentEXT),
|
||||||
|
SYM(PatchParameteriEXT),
|
||||||
|
SYM(TexParameterIivEXT),
|
||||||
|
SYM(TexParameterIuivEXT),
|
||||||
|
SYM(GetTexParameterIivEXT),
|
||||||
|
SYM(GetTexParameterIuivEXT),
|
||||||
|
SYM(SamplerParameterIivEXT),
|
||||||
|
SYM(SamplerParameterIuivEXT),
|
||||||
|
SYM(GetSamplerParameterIivEXT),
|
||||||
|
SYM(GetSamplerParameterIuivEXT),
|
||||||
|
SYM(TexBufferEXT),
|
||||||
|
SYM(TexBufferRangeEXT),
|
||||||
|
SYM(TexStorage1DEXT),
|
||||||
|
SYM(TexStorage2DEXT),
|
||||||
|
SYM(TexStorage3DEXT),
|
||||||
|
SYM(TextureStorage1DEXT),
|
||||||
|
SYM(TextureStorage2DEXT),
|
||||||
|
SYM(TextureStorage3DEXT),
|
||||||
|
SYM(TextureViewEXT),
|
||||||
|
SYM(FramebufferTextureMultiviewOVR),
|
||||||
|
SYM(FramebufferTextureMultisampleMultiviewOVR),
|
||||||
|
|
||||||
|
{ NULL, NULL },
|
||||||
|
};
|
||||||
|
RGLSYMGLBLENDBARRIERKHRPROC __rglgen_glBlendBarrierKHR;
|
||||||
|
RGLSYMGLDEBUGMESSAGECONTROLKHRPROC __rglgen_glDebugMessageControlKHR;
|
||||||
|
RGLSYMGLDEBUGMESSAGEINSERTKHRPROC __rglgen_glDebugMessageInsertKHR;
|
||||||
|
RGLSYMGLDEBUGMESSAGECALLBACKKHRPROC __rglgen_glDebugMessageCallbackKHR;
|
||||||
|
RGLSYMGLGETDEBUGMESSAGELOGKHRPROC __rglgen_glGetDebugMessageLogKHR;
|
||||||
|
RGLSYMGLPUSHDEBUGGROUPKHRPROC __rglgen_glPushDebugGroupKHR;
|
||||||
|
RGLSYMGLPOPDEBUGGROUPKHRPROC __rglgen_glPopDebugGroupKHR;
|
||||||
|
RGLSYMGLOBJECTLABELKHRPROC __rglgen_glObjectLabelKHR;
|
||||||
|
RGLSYMGLGETOBJECTLABELKHRPROC __rglgen_glGetObjectLabelKHR;
|
||||||
|
RGLSYMGLOBJECTPTRLABELKHRPROC __rglgen_glObjectPtrLabelKHR;
|
||||||
|
RGLSYMGLGETOBJECTPTRLABELKHRPROC __rglgen_glGetObjectPtrLabelKHR;
|
||||||
|
RGLSYMGLGETPOINTERVKHRPROC __rglgen_glGetPointervKHR;
|
||||||
|
RGLSYMGLGETGRAPHICSRESETSTATUSKHRPROC __rglgen_glGetGraphicsResetStatusKHR;
|
||||||
|
RGLSYMGLREADNPIXELSKHRPROC __rglgen_glReadnPixelsKHR;
|
||||||
|
RGLSYMGLGETNUNIFORMFVKHRPROC __rglgen_glGetnUniformfvKHR;
|
||||||
|
RGLSYMGLGETNUNIFORMIVKHRPROC __rglgen_glGetnUniformivKHR;
|
||||||
|
RGLSYMGLGETNUNIFORMUIVKHRPROC __rglgen_glGetnUniformuivKHR;
|
||||||
|
RGLSYMGLEGLIMAGETARGETTEXTURE2DOESPROC __rglgen_glEGLImageTargetTexture2DOES;
|
||||||
|
RGLSYMGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC __rglgen_glEGLImageTargetRenderbufferStorageOES;
|
||||||
|
RGLSYMGLCOPYIMAGESUBDATAOESPROC __rglgen_glCopyImageSubDataOES;
|
||||||
|
RGLSYMGLENABLEIOESPROC __rglgen_glEnableiOES;
|
||||||
|
RGLSYMGLDISABLEIOESPROC __rglgen_glDisableiOES;
|
||||||
|
RGLSYMGLBLENDEQUATIONIOESPROC __rglgen_glBlendEquationiOES;
|
||||||
|
RGLSYMGLBLENDEQUATIONSEPARATEIOESPROC __rglgen_glBlendEquationSeparateiOES;
|
||||||
|
RGLSYMGLBLENDFUNCIOESPROC __rglgen_glBlendFunciOES;
|
||||||
|
RGLSYMGLBLENDFUNCSEPARATEIOESPROC __rglgen_glBlendFuncSeparateiOES;
|
||||||
|
RGLSYMGLCOLORMASKIOESPROC __rglgen_glColorMaskiOES;
|
||||||
|
RGLSYMGLISENABLEDIOESPROC __rglgen_glIsEnablediOES;
|
||||||
|
RGLSYMGLDRAWELEMENTSBASEVERTEXOESPROC __rglgen_glDrawElementsBaseVertexOES;
|
||||||
|
RGLSYMGLDRAWRANGEELEMENTSBASEVERTEXOESPROC __rglgen_glDrawRangeElementsBaseVertexOES;
|
||||||
|
RGLSYMGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC __rglgen_glDrawElementsInstancedBaseVertexOES;
|
||||||
|
RGLSYMGLMULTIDRAWELEMENTSBASEVERTEXOESPROC __rglgen_glMultiDrawElementsBaseVertexOES;
|
||||||
|
RGLSYMGLFRAMEBUFFERTEXTUREOESPROC __rglgen_glFramebufferTextureOES;
|
||||||
|
RGLSYMGLGETPROGRAMBINARYOESPROC __rglgen_glGetProgramBinaryOES;
|
||||||
|
RGLSYMGLPROGRAMBINARYOESPROC __rglgen_glProgramBinaryOES;
|
||||||
|
RGLSYMGLMAPBUFFEROESPROC __rglgen_glMapBufferOES;
|
||||||
|
RGLSYMGLUNMAPBUFFEROESPROC __rglgen_glUnmapBufferOES;
|
||||||
|
RGLSYMGLGETBUFFERPOINTERVOESPROC __rglgen_glGetBufferPointervOES;
|
||||||
|
RGLSYMGLPRIMITIVEBOUNDINGBOXOESPROC __rglgen_glPrimitiveBoundingBoxOES;
|
||||||
|
RGLSYMGLMINSAMPLESHADINGOESPROC __rglgen_glMinSampleShadingOES;
|
||||||
|
RGLSYMGLPATCHPARAMETERIOESPROC __rglgen_glPatchParameteriOES;
|
||||||
|
RGLSYMGLTEXIMAGE3DOESPROC __rglgen_glTexImage3DOES;
|
||||||
|
RGLSYMGLTEXSUBIMAGE3DOESPROC __rglgen_glTexSubImage3DOES;
|
||||||
|
RGLSYMGLCOPYTEXSUBIMAGE3DOESPROC __rglgen_glCopyTexSubImage3DOES;
|
||||||
|
RGLSYMGLCOMPRESSEDTEXIMAGE3DOESPROC __rglgen_glCompressedTexImage3DOES;
|
||||||
|
RGLSYMGLCOMPRESSEDTEXSUBIMAGE3DOESPROC __rglgen_glCompressedTexSubImage3DOES;
|
||||||
|
RGLSYMGLFRAMEBUFFERTEXTURE3DOESPROC __rglgen_glFramebufferTexture3DOES;
|
||||||
|
RGLSYMGLTEXPARAMETERIIVOESPROC __rglgen_glTexParameterIivOES;
|
||||||
|
RGLSYMGLTEXPARAMETERIUIVOESPROC __rglgen_glTexParameterIuivOES;
|
||||||
|
RGLSYMGLGETTEXPARAMETERIIVOESPROC __rglgen_glGetTexParameterIivOES;
|
||||||
|
RGLSYMGLGETTEXPARAMETERIUIVOESPROC __rglgen_glGetTexParameterIuivOES;
|
||||||
|
RGLSYMGLSAMPLERPARAMETERIIVOESPROC __rglgen_glSamplerParameterIivOES;
|
||||||
|
RGLSYMGLSAMPLERPARAMETERIUIVOESPROC __rglgen_glSamplerParameterIuivOES;
|
||||||
|
RGLSYMGLGETSAMPLERPARAMETERIIVOESPROC __rglgen_glGetSamplerParameterIivOES;
|
||||||
|
RGLSYMGLGETSAMPLERPARAMETERIUIVOESPROC __rglgen_glGetSamplerParameterIuivOES;
|
||||||
|
RGLSYMGLTEXBUFFEROESPROC __rglgen_glTexBufferOES;
|
||||||
|
RGLSYMGLTEXBUFFERRANGEOESPROC __rglgen_glTexBufferRangeOES;
|
||||||
|
RGLSYMGLTEXSTORAGE3DMULTISAMPLEOESPROC __rglgen_glTexStorage3DMultisampleOES;
|
||||||
|
RGLSYMGLTEXTUREVIEWOESPROC __rglgen_glTextureViewOES;
|
||||||
|
RGLSYMGLBINDVERTEXARRAYOESPROC __rglgen_glBindVertexArrayOES;
|
||||||
|
RGLSYMGLDELETEVERTEXARRAYSOESPROC __rglgen_glDeleteVertexArraysOES;
|
||||||
|
RGLSYMGLGENVERTEXARRAYSOESPROC __rglgen_glGenVertexArraysOES;
|
||||||
|
RGLSYMGLISVERTEXARRAYOESPROC __rglgen_glIsVertexArrayOES;
|
||||||
|
RGLSYMGLVIEWPORTARRAYVOESPROC __rglgen_glViewportArrayvOES;
|
||||||
|
RGLSYMGLVIEWPORTINDEXEDFOESPROC __rglgen_glViewportIndexedfOES;
|
||||||
|
RGLSYMGLVIEWPORTINDEXEDFVOESPROC __rglgen_glViewportIndexedfvOES;
|
||||||
|
RGLSYMGLSCISSORARRAYVOESPROC __rglgen_glScissorArrayvOES;
|
||||||
|
RGLSYMGLSCISSORINDEXEDOESPROC __rglgen_glScissorIndexedOES;
|
||||||
|
RGLSYMGLSCISSORINDEXEDVOESPROC __rglgen_glScissorIndexedvOES;
|
||||||
|
RGLSYMGLDEPTHRANGEARRAYFVOESPROC __rglgen_glDepthRangeArrayfvOES;
|
||||||
|
RGLSYMGLDEPTHRANGEINDEXEDFOESPROC __rglgen_glDepthRangeIndexedfOES;
|
||||||
|
RGLSYMGLGETFLOATI_VOESPROC __rglgen_glGetFloati_vOES;
|
||||||
|
RGLSYMGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC __rglgen_glDrawArraysInstancedBaseInstanceEXT;
|
||||||
|
RGLSYMGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC __rglgen_glDrawElementsInstancedBaseInstanceEXT;
|
||||||
|
RGLSYMGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC __rglgen_glDrawElementsInstancedBaseVertexBaseInstanceEXT;
|
||||||
|
RGLSYMGLBINDFRAGDATALOCATIONINDEXEDEXTPROC __rglgen_glBindFragDataLocationIndexedEXT;
|
||||||
|
RGLSYMGLBINDFRAGDATALOCATIONEXTPROC __rglgen_glBindFragDataLocationEXT;
|
||||||
|
RGLSYMGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC __rglgen_glGetProgramResourceLocationIndexEXT;
|
||||||
|
RGLSYMGLGETFRAGDATAINDEXEXTPROC __rglgen_glGetFragDataIndexEXT;
|
||||||
|
RGLSYMGLBUFFERSTORAGEEXTPROC __rglgen_glBufferStorageEXT;
|
||||||
|
RGLSYMGLCLEARTEXIMAGEEXTPROC __rglgen_glClearTexImageEXT;
|
||||||
|
RGLSYMGLCLEARTEXSUBIMAGEEXTPROC __rglgen_glClearTexSubImageEXT;
|
||||||
|
RGLSYMGLCOPYIMAGESUBDATAEXTPROC __rglgen_glCopyImageSubDataEXT;
|
||||||
|
RGLSYMGLLABELOBJECTEXTPROC __rglgen_glLabelObjectEXT;
|
||||||
|
RGLSYMGLGETOBJECTLABELEXTPROC __rglgen_glGetObjectLabelEXT;
|
||||||
|
RGLSYMGLINSERTEVENTMARKEREXTPROC __rglgen_glInsertEventMarkerEXT;
|
||||||
|
RGLSYMGLPUSHGROUPMARKEREXTPROC __rglgen_glPushGroupMarkerEXT;
|
||||||
|
RGLSYMGLPOPGROUPMARKEREXTPROC __rglgen_glPopGroupMarkerEXT;
|
||||||
|
RGLSYMGLDISCARDFRAMEBUFFEREXTPROC __rglgen_glDiscardFramebufferEXT;
|
||||||
|
RGLSYMGLGENQUERIESEXTPROC __rglgen_glGenQueriesEXT;
|
||||||
|
RGLSYMGLDELETEQUERIESEXTPROC __rglgen_glDeleteQueriesEXT;
|
||||||
|
RGLSYMGLISQUERYEXTPROC __rglgen_glIsQueryEXT;
|
||||||
|
RGLSYMGLBEGINQUERYEXTPROC __rglgen_glBeginQueryEXT;
|
||||||
|
RGLSYMGLENDQUERYEXTPROC __rglgen_glEndQueryEXT;
|
||||||
|
RGLSYMGLQUERYCOUNTEREXTPROC __rglgen_glQueryCounterEXT;
|
||||||
|
RGLSYMGLGETQUERYIVEXTPROC __rglgen_glGetQueryivEXT;
|
||||||
|
RGLSYMGLGETQUERYOBJECTIVEXTPROC __rglgen_glGetQueryObjectivEXT;
|
||||||
|
RGLSYMGLGETQUERYOBJECTUIVEXTPROC __rglgen_glGetQueryObjectuivEXT;
|
||||||
|
RGLSYMGLDRAWBUFFERSEXTPROC __rglgen_glDrawBuffersEXT;
|
||||||
|
RGLSYMGLENABLEIEXTPROC __rglgen_glEnableiEXT;
|
||||||
|
RGLSYMGLDISABLEIEXTPROC __rglgen_glDisableiEXT;
|
||||||
|
RGLSYMGLBLENDEQUATIONIEXTPROC __rglgen_glBlendEquationiEXT;
|
||||||
|
RGLSYMGLBLENDEQUATIONSEPARATEIEXTPROC __rglgen_glBlendEquationSeparateiEXT;
|
||||||
|
RGLSYMGLBLENDFUNCIEXTPROC __rglgen_glBlendFunciEXT;
|
||||||
|
RGLSYMGLBLENDFUNCSEPARATEIEXTPROC __rglgen_glBlendFuncSeparateiEXT;
|
||||||
|
RGLSYMGLCOLORMASKIEXTPROC __rglgen_glColorMaskiEXT;
|
||||||
|
RGLSYMGLISENABLEDIEXTPROC __rglgen_glIsEnablediEXT;
|
||||||
|
RGLSYMGLDRAWELEMENTSBASEVERTEXEXTPROC __rglgen_glDrawElementsBaseVertexEXT;
|
||||||
|
RGLSYMGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC __rglgen_glDrawRangeElementsBaseVertexEXT;
|
||||||
|
RGLSYMGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC __rglgen_glDrawElementsInstancedBaseVertexEXT;
|
||||||
|
RGLSYMGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC __rglgen_glMultiDrawElementsBaseVertexEXT;
|
||||||
|
RGLSYMGLDRAWARRAYSINSTANCEDEXTPROC __rglgen_glDrawArraysInstancedEXT;
|
||||||
|
RGLSYMGLDRAWELEMENTSINSTANCEDEXTPROC __rglgen_glDrawElementsInstancedEXT;
|
||||||
|
RGLSYMGLFRAMEBUFFERTEXTUREEXTPROC __rglgen_glFramebufferTextureEXT;
|
||||||
|
RGLSYMGLVERTEXATTRIBDIVISOREXTPROC __rglgen_glVertexAttribDivisorEXT;
|
||||||
|
RGLSYMGLMAPBUFFERRANGEEXTPROC __rglgen_glMapBufferRangeEXT;
|
||||||
|
RGLSYMGLFLUSHMAPPEDBUFFERRANGEEXTPROC __rglgen_glFlushMappedBufferRangeEXT;
|
||||||
|
RGLSYMGLMULTIDRAWARRAYSEXTPROC __rglgen_glMultiDrawArraysEXT;
|
||||||
|
RGLSYMGLMULTIDRAWELEMENTSEXTPROC __rglgen_glMultiDrawElementsEXT;
|
||||||
|
RGLSYMGLMULTIDRAWARRAYSINDIRECTEXTPROC __rglgen_glMultiDrawArraysIndirectEXT;
|
||||||
|
RGLSYMGLMULTIDRAWELEMENTSINDIRECTEXTPROC __rglgen_glMultiDrawElementsIndirectEXT;
|
||||||
|
RGLSYMGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC __rglgen_glRenderbufferStorageMultisampleEXT;
|
||||||
|
RGLSYMGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC __rglgen_glFramebufferTexture2DMultisampleEXT;
|
||||||
|
RGLSYMGLREADBUFFERINDEXEDEXTPROC __rglgen_glReadBufferIndexedEXT;
|
||||||
|
RGLSYMGLDRAWBUFFERSINDEXEDEXTPROC __rglgen_glDrawBuffersIndexedEXT;
|
||||||
|
RGLSYMGLGETINTEGERI_VEXTPROC __rglgen_glGetIntegeri_vEXT;
|
||||||
|
RGLSYMGLPOLYGONOFFSETCLAMPEXTPROC __rglgen_glPolygonOffsetClampEXT;
|
||||||
|
RGLSYMGLPRIMITIVEBOUNDINGBOXEXTPROC __rglgen_glPrimitiveBoundingBoxEXT;
|
||||||
|
RGLSYMGLRASTERSAMPLESEXTPROC __rglgen_glRasterSamplesEXT;
|
||||||
|
RGLSYMGLGETGRAPHICSRESETSTATUSEXTPROC __rglgen_glGetGraphicsResetStatusEXT;
|
||||||
|
RGLSYMGLREADNPIXELSEXTPROC __rglgen_glReadnPixelsEXT;
|
||||||
|
RGLSYMGLGETNUNIFORMFVEXTPROC __rglgen_glGetnUniformfvEXT;
|
||||||
|
RGLSYMGLGETNUNIFORMIVEXTPROC __rglgen_glGetnUniformivEXT;
|
||||||
|
RGLSYMGLACTIVESHADERPROGRAMEXTPROC __rglgen_glActiveShaderProgramEXT;
|
||||||
|
RGLSYMGLBINDPROGRAMPIPELINEEXTPROC __rglgen_glBindProgramPipelineEXT;
|
||||||
|
RGLSYMGLCREATESHADERPROGRAMVEXTPROC __rglgen_glCreateShaderProgramvEXT;
|
||||||
|
RGLSYMGLDELETEPROGRAMPIPELINESEXTPROC __rglgen_glDeleteProgramPipelinesEXT;
|
||||||
|
RGLSYMGLGENPROGRAMPIPELINESEXTPROC __rglgen_glGenProgramPipelinesEXT;
|
||||||
|
RGLSYMGLGETPROGRAMPIPELINEINFOLOGEXTPROC __rglgen_glGetProgramPipelineInfoLogEXT;
|
||||||
|
RGLSYMGLGETPROGRAMPIPELINEIVEXTPROC __rglgen_glGetProgramPipelineivEXT;
|
||||||
|
RGLSYMGLISPROGRAMPIPELINEEXTPROC __rglgen_glIsProgramPipelineEXT;
|
||||||
|
RGLSYMGLPROGRAMPARAMETERIEXTPROC __rglgen_glProgramParameteriEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM1FEXTPROC __rglgen_glProgramUniform1fEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM1FVEXTPROC __rglgen_glProgramUniform1fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM1IEXTPROC __rglgen_glProgramUniform1iEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM1IVEXTPROC __rglgen_glProgramUniform1ivEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM2FEXTPROC __rglgen_glProgramUniform2fEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM2FVEXTPROC __rglgen_glProgramUniform2fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM2IEXTPROC __rglgen_glProgramUniform2iEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM2IVEXTPROC __rglgen_glProgramUniform2ivEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM3FEXTPROC __rglgen_glProgramUniform3fEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM3FVEXTPROC __rglgen_glProgramUniform3fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM3IEXTPROC __rglgen_glProgramUniform3iEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM3IVEXTPROC __rglgen_glProgramUniform3ivEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM4FEXTPROC __rglgen_glProgramUniform4fEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM4FVEXTPROC __rglgen_glProgramUniform4fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM4IEXTPROC __rglgen_glProgramUniform4iEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM4IVEXTPROC __rglgen_glProgramUniform4ivEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX2FVEXTPROC __rglgen_glProgramUniformMatrix2fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX3FVEXTPROC __rglgen_glProgramUniformMatrix3fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX4FVEXTPROC __rglgen_glProgramUniformMatrix4fvEXT;
|
||||||
|
RGLSYMGLUSEPROGRAMSTAGESEXTPROC __rglgen_glUseProgramStagesEXT;
|
||||||
|
RGLSYMGLVALIDATEPROGRAMPIPELINEEXTPROC __rglgen_glValidateProgramPipelineEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM1UIEXTPROC __rglgen_glProgramUniform1uiEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM2UIEXTPROC __rglgen_glProgramUniform2uiEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM3UIEXTPROC __rglgen_glProgramUniform3uiEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM4UIEXTPROC __rglgen_glProgramUniform4uiEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM1UIVEXTPROC __rglgen_glProgramUniform1uivEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM2UIVEXTPROC __rglgen_glProgramUniform2uivEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM3UIVEXTPROC __rglgen_glProgramUniform3uivEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORM4UIVEXTPROC __rglgen_glProgramUniform4uivEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC __rglgen_glProgramUniformMatrix2x3fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC __rglgen_glProgramUniformMatrix3x2fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC __rglgen_glProgramUniformMatrix2x4fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC __rglgen_glProgramUniformMatrix4x2fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC __rglgen_glProgramUniformMatrix3x4fvEXT;
|
||||||
|
RGLSYMGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC __rglgen_glProgramUniformMatrix4x3fvEXT;
|
||||||
|
RGLSYMGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC __rglgen_glFramebufferPixelLocalStorageSizeEXT;
|
||||||
|
RGLSYMGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC __rglgen_glGetFramebufferPixelLocalStorageSizeEXT;
|
||||||
|
RGLSYMGLCLEARPIXELLOCALSTORAGEUIEXTPROC __rglgen_glClearPixelLocalStorageuiEXT;
|
||||||
|
RGLSYMGLTEXPAGECOMMITMENTEXTPROC __rglgen_glTexPageCommitmentEXT;
|
||||||
|
RGLSYMGLPATCHPARAMETERIEXTPROC __rglgen_glPatchParameteriEXT;
|
||||||
|
RGLSYMGLTEXPARAMETERIIVEXTPROC __rglgen_glTexParameterIivEXT;
|
||||||
|
RGLSYMGLTEXPARAMETERIUIVEXTPROC __rglgen_glTexParameterIuivEXT;
|
||||||
|
RGLSYMGLGETTEXPARAMETERIIVEXTPROC __rglgen_glGetTexParameterIivEXT;
|
||||||
|
RGLSYMGLGETTEXPARAMETERIUIVEXTPROC __rglgen_glGetTexParameterIuivEXT;
|
||||||
|
RGLSYMGLSAMPLERPARAMETERIIVEXTPROC __rglgen_glSamplerParameterIivEXT;
|
||||||
|
RGLSYMGLSAMPLERPARAMETERIUIVEXTPROC __rglgen_glSamplerParameterIuivEXT;
|
||||||
|
RGLSYMGLGETSAMPLERPARAMETERIIVEXTPROC __rglgen_glGetSamplerParameterIivEXT;
|
||||||
|
RGLSYMGLGETSAMPLERPARAMETERIUIVEXTPROC __rglgen_glGetSamplerParameterIuivEXT;
|
||||||
|
RGLSYMGLTEXBUFFEREXTPROC __rglgen_glTexBufferEXT;
|
||||||
|
RGLSYMGLTEXBUFFERRANGEEXTPROC __rglgen_glTexBufferRangeEXT;
|
||||||
|
RGLSYMGLTEXSTORAGE1DEXTPROC __rglgen_glTexStorage1DEXT;
|
||||||
|
RGLSYMGLTEXSTORAGE2DEXTPROC __rglgen_glTexStorage2DEXT;
|
||||||
|
RGLSYMGLTEXSTORAGE3DEXTPROC __rglgen_glTexStorage3DEXT;
|
||||||
|
RGLSYMGLTEXTURESTORAGE1DEXTPROC __rglgen_glTextureStorage1DEXT;
|
||||||
|
RGLSYMGLTEXTURESTORAGE2DEXTPROC __rglgen_glTextureStorage2DEXT;
|
||||||
|
RGLSYMGLTEXTURESTORAGE3DEXTPROC __rglgen_glTextureStorage3DEXT;
|
||||||
|
RGLSYMGLTEXTUREVIEWEXTPROC __rglgen_glTextureViewEXT;
|
||||||
|
RGLSYMGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC __rglgen_glFramebufferTextureMultiviewOVR;
|
||||||
|
RGLSYMGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC __rglgen_glFramebufferTextureMultisampleMultiviewOVR;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue