mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-12-27 03:31:49 +01:00
added all common
This commit is contained in:
parent
7d6e45733a
commit
6c1487acb0
1
libretro-common/.gitignore
vendored
Normal file
1
libretro-common/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.o
|
314
libretro-common/audio/audio_mix.c
Normal file
314
libretro-common/audio/audio_mix.c
Normal file
@ -0,0 +1,314 @@
|
||||
/* 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;
|
||||
audio_chunk_t *chunk = (audio_chunk_t*)calloc(1, sizeof(*chunk));
|
||||
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
|
||||
chunk->sample_rate = sample_rate;
|
||||
|
||||
if (!filestream_read_file(path, &chunk->buf, &chunk->len))
|
||||
{
|
||||
printf("Could not open WAV file for reading.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
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 != chunk->rwav->samplerate)
|
||||
{
|
||||
chunk->resample = true;
|
||||
chunk->ratio = (double)sample_rate / chunk->rwav->samplerate;
|
||||
|
||||
retro_resampler_realloc(&chunk->resampler_data,
|
||||
&chunk->resampler,
|
||||
NULL,
|
||||
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;
|
||||
}
|
652
libretro-common/audio/audio_mixer.c
Normal file
652
libretro-common/audio/audio_mixer.c
Normal file
@ -0,0 +1,652 @@
|
||||
/* 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 <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 "../../deps/stb/stb_vorbis.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
|
||||
} 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
|
||||
{
|
||||
/* wav */
|
||||
unsigned position;
|
||||
} wav;
|
||||
|
||||
#ifdef HAVE_STB_VORBIS
|
||||
struct
|
||||
{
|
||||
/* ogg */
|
||||
unsigned position;
|
||||
unsigned samples;
|
||||
unsigned buf_samples;
|
||||
float* buffer;
|
||||
float ratio;
|
||||
stb_vorbis *stream;
|
||||
void *resampler_data;
|
||||
const retro_resampler_t *resampler;
|
||||
} ogg;
|
||||
#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, 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
|
||||
}
|
||||
|
||||
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_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, 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
|
||||
|
||||
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_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
|
||||
|
||||
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_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;
|
||||
}
|
||||
}
|
160
libretro-common/audio/conversion/float_to_s16.c
Normal file
160
libretro-common/audio/conversion/float_to_s16.c
Normal file
@ -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
|
||||
}
|
69
libretro-common/audio/conversion/float_to_s16_neon.S
Normal file
69
libretro-common/audio/conversion/float_to_s16_neon.S
Normal file
@ -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
|
64
libretro-common/audio/conversion/float_to_s16_neon.c
Normal file
64
libretro-common/audio/conversion/float_to_s16_neon.c
Normal file
@ -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
|
189
libretro-common/audio/conversion/s16_to_float.c
Normal file
189
libretro-common/audio/conversion/s16_to_float.c
Normal file
@ -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
|
||||
}
|
76
libretro-common/audio/conversion/s16_to_float_neon.S
Normal file
76
libretro-common/audio/conversion/s16_to_float_neon.S
Normal file
@ -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
|
71
libretro-common/audio/conversion/s16_to_float_neon.c
Normal file
71
libretro-common/audio/conversion/s16_to_float_neon.c
Normal file
@ -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
|
324
libretro-common/audio/dsp_filter.c
Normal file
324
libretro-common/audio/dsp_filter.c
Normal file
@ -0,0 +1,324 @@
|
||||
/* 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();
|
||||
|
||||
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;
|
||||
}
|
12
libretro-common/audio/dsp_filters/BassBoost.dsp
Normal file
12
libretro-common/audio/dsp_filters/BassBoost.dsp
Normal file
@ -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"
|
||||
|
22
libretro-common/audio/dsp_filters/ChipTuneEnhance.dsp
Normal file
22
libretro-common/audio/dsp_filters/ChipTuneEnhance.dsp
Normal file
@ -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"
|
14
libretro-common/audio/dsp_filters/Chorus.dsp
Normal file
14
libretro-common/audio/dsp_filters/Chorus.dsp
Normal file
@ -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
|
41
libretro-common/audio/dsp_filters/EQ.dsp
Normal file
41
libretro-common/audio/dsp_filters/EQ.dsp
Normal file
@ -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
|
19
libretro-common/audio/dsp_filters/Echo.dsp
Normal file
19
libretro-common/audio/dsp_filters/Echo.dsp
Normal file
@ -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"
|
12
libretro-common/audio/dsp_filters/EchoReverb.dsp
Normal file
12
libretro-common/audio/dsp_filters/EchoReverb.dsp
Normal file
@ -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
|
7
libretro-common/audio/dsp_filters/HighShelfDampen.dsp
Normal file
7
libretro-common/audio/dsp_filters/HighShelfDampen.dsp
Normal file
@ -0,0 +1,7 @@
|
||||
filters = 1
|
||||
filter0 = iir
|
||||
|
||||
iir_gain = -12.0
|
||||
iir_type = HSH
|
||||
iir_frequency = 8000.0
|
||||
|
23
libretro-common/audio/dsp_filters/IIR.dsp
Normal file
23
libretro-common/audio/dsp_filters/IIR.dsp
Normal file
@ -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
|
||||
|
47
libretro-common/audio/dsp_filters/LowPassCPS.dsp
Normal file
47
libretro-common/audio/dsp_filters/LowPassCPS.dsp
Normal file
@ -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
|
91
libretro-common/audio/dsp_filters/Makefile
Normal file
91
libretro-common/audio/dsp_filters/Makefile
Normal file
@ -0,0 +1,91 @@
|
||||
compiler := gcc
|
||||
extra_flags :=
|
||||
use_neon := 0
|
||||
build = release
|
||||
DYLIB := so
|
||||
|
||||
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)
|
12
libretro-common/audio/dsp_filters/Mono.dsp
Normal file
12
libretro-common/audio/dsp_filters/Mono.dsp
Normal file
@ -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"
|
23
libretro-common/audio/dsp_filters/Panning.dsp
Normal file
23
libretro-common/audio/dsp_filters/Panning.dsp
Normal file
@ -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"
|
||||
|
11
libretro-common/audio/dsp_filters/Phaser.dsp
Normal file
11
libretro-common/audio/dsp_filters/Phaser.dsp
Normal file
@ -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
|
||||
|
10
libretro-common/audio/dsp_filters/Reverb.dsp
Normal file
10
libretro-common/audio/dsp_filters/Reverb.dsp
Normal file
@ -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
|
||||
|
10
libretro-common/audio/dsp_filters/WahWah.dsp
Normal file
10
libretro-common/audio/dsp_filters/WahWah.dsp
Normal file
@ -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
|
||||
|
161
libretro-common/audio/dsp_filters/chorus.c
Normal file
161
libretro-common/audio/dsp_filters/chorus.c
Normal file
@ -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
|
||||
|
182
libretro-common/audio/dsp_filters/echo.c
Normal file
182
libretro-common/audio/dsp_filters/echo.c
Normal file
@ -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
|
||||
|
352
libretro-common/audio/dsp_filters/eq.c
Normal file
352
libretro-common/audio/dsp_filters/eq.c
Normal file
@ -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
|
||||
|
205
libretro-common/audio/dsp_filters/fft/fft.c
Normal file
205
libretro-common/audio/dsp_filters/fft/fft.c
Normal file
@ -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)
|
||||
{
|
||||
fft_t *fft = (fft_t*)calloc(1, sizeof(*fft));
|
||||
if (!fft)
|
||||
return NULL;
|
||||
|
||||
unsigned 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);
|
||||
}
|
||||
|
46
libretro-common/audio/dsp_filters/fft/fft.h
Normal file
46
libretro-common/audio/dsp_filters/fft/fft.h
Normal file
@ -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
|
||||
|
372
libretro-common/audio/dsp_filters/iir.c
Normal file
372
libretro-common/audio/dsp_filters/iir.c
Normal file
@ -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
|
||||
|
4
libretro-common/audio/dsp_filters/link.T
Normal file
4
libretro-common/audio/dsp_filters/link.T
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
global: dspfilter_get_implementation;
|
||||
local: *;
|
||||
};
|
112
libretro-common/audio/dsp_filters/panning.c
Normal file
112
libretro-common/audio/dsp_filters/panning.c
Normal file
@ -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
|
||||
|
146
libretro-common/audio/dsp_filters/phaser.c
Normal file
146
libretro-common/audio/dsp_filters/phaser.c
Normal file
@ -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
|
||||
|
327
libretro-common/audio/dsp_filters/reverb.c
Normal file
327
libretro-common/audio/dsp_filters/reverb.c
Normal file
@ -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
|
||||
|
||||
|
148
libretro-common/audio/dsp_filters/wahwah.c
Normal file
148
libretro-common/audio/dsp_filters/wahwah.c
Normal file
@ -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
|
||||
|
170
libretro-common/audio/resampler/audio_resampler.c
Normal file
170
libretro-common/audio/resampler/audio_resampler.c
Normal file
@ -0,0 +1,170 @@
|
||||
/* 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,
|
||||
double bw_ratio)
|
||||
{
|
||||
resampler_simd_mask_t mask = (resampler_simd_mask_t)cpu_features_get();
|
||||
|
||||
*re = (*backend)->init(&resampler_config, bw_ratio, 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, double bw_ratio)
|
||||
{
|
||||
if (*re && *backend)
|
||||
(*backend)->free(*re);
|
||||
|
||||
*re = NULL;
|
||||
*backend = find_resampler_driver(ident);
|
||||
|
||||
if (!resampler_append_plugs(re, backend, bw_ratio))
|
||||
{
|
||||
if (!*re)
|
||||
*backend = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
88
libretro-common/audio/resampler/drivers/nearest_resampler.c
Normal file
88
libretro-common/audio/resampler/drivers/nearest_resampler.c
Normal file
@ -0,0 +1,88 @@
|
||||
/* 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, 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"
|
||||
};
|
56
libretro-common/audio/resampler/drivers/null_resampler.c
Normal file
56
libretro-common/audio/resampler/drivers/null_resampler.c
Normal file
@ -0,0 +1,56 @@
|
||||
/* 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, 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"
|
||||
};
|
589
libretro-common/audio/resampler/drivers/sinc_resampler.c
Normal file
589
libretro-common/audio/resampler/drivers/sinc_resampler.c
Normal file
@ -0,0 +1,589 @@
|
||||
/* 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__) && ENABLE_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. */
|
||||
#if defined(SINC_LOWEST_QUALITY)
|
||||
#define SINC_WINDOW_LANCZOS
|
||||
#define CUTOFF 0.98
|
||||
#define PHASE_BITS 12
|
||||
#define SINC_COEFF_LERP 0
|
||||
#define SUBPHASE_BITS 10
|
||||
#define SIDELOBES 2
|
||||
#define ENABLE_AVX 0
|
||||
#elif defined(SINC_LOWER_QUALITY)
|
||||
#define SINC_WINDOW_LANCZOS
|
||||
#define CUTOFF 0.98
|
||||
#define PHASE_BITS 12
|
||||
#define SUBPHASE_BITS 10
|
||||
#define SINC_COEFF_LERP 0
|
||||
#define SIDELOBES 4
|
||||
#define ENABLE_AVX 0
|
||||
#elif defined(SINC_HIGHER_QUALITY)
|
||||
#define SINC_WINDOW_KAISER
|
||||
#define SINC_WINDOW_KAISER_BETA 10.5
|
||||
#define CUTOFF 0.90
|
||||
#define PHASE_BITS 10
|
||||
#define SUBPHASE_BITS 14
|
||||
#define SINC_COEFF_LERP 1
|
||||
#define SIDELOBES 32
|
||||
#define ENABLE_AVX 1
|
||||
#elif defined(SINC_HIGHEST_QUALITY)
|
||||
#define SINC_WINDOW_KAISER
|
||||
#define SINC_WINDOW_KAISER_BETA 14.5
|
||||
#define CUTOFF 0.962
|
||||
#define PHASE_BITS 10
|
||||
#define SUBPHASE_BITS 14
|
||||
#define SINC_COEFF_LERP 1
|
||||
#define SIDELOBES 128
|
||||
#define ENABLE_AVX 1
|
||||
#else
|
||||
#define SINC_WINDOW_KAISER
|
||||
#define SINC_WINDOW_KAISER_BETA 5.5
|
||||
#define CUTOFF 0.825
|
||||
#define PHASE_BITS 8
|
||||
#define SUBPHASE_BITS 16
|
||||
#define SINC_COEFF_LERP 1
|
||||
#define SIDELOBES 8
|
||||
#define ENABLE_AVX 0
|
||||
#endif
|
||||
|
||||
#if SINC_COEFF_LERP
|
||||
#define TAPS_MULT 2
|
||||
#else
|
||||
#define TAPS_MULT 1
|
||||
#endif
|
||||
|
||||
#if defined(SINC_WINDOW_LANCZOS)
|
||||
#define window_function(idx) (lanzcos_window_function(idx))
|
||||
#elif defined(SINC_WINDOW_KAISER)
|
||||
#define window_function(idx) (kaiser_window_function(idx, SINC_WINDOW_KAISER_BETA))
|
||||
#else
|
||||
#error "No SINC window function defined."
|
||||
#endif
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#define PHASES (1 << (PHASE_BITS + SUBPHASE_BITS))
|
||||
|
||||
#define TAPS (SIDELOBES * 2)
|
||||
#define SUBPHASE_MASK ((1 << SUBPHASE_BITS) - 1)
|
||||
#define SUBPHASE_MOD (1.0f / (1 << SUBPHASE_BITS))
|
||||
|
||||
typedef struct rarch_sinc_resampler
|
||||
{
|
||||
float *phase_table;
|
||||
float *buffer_l;
|
||||
float *buffer_r;
|
||||
|
||||
unsigned taps;
|
||||
|
||||
unsigned ptr;
|
||||
uint32_t time;
|
||||
|
||||
/* 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;
|
||||
} rarch_sinc_resampler_t;
|
||||
|
||||
#if defined(__ARM_NEON__) && !defined(SINC_COEFF_LERP)
|
||||
/* 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_;
|
||||
|
||||
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;
|
||||
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 >> 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__) && ENABLE_AVX
|
||||
static void resampler_sinc_process_avx(void *re_, struct resampler_data *data)
|
||||
{
|
||||
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
|
||||
|
||||
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;
|
||||
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 >> SUBPHASE_BITS;
|
||||
const float *phase_table = resamp->phase_table + phase * taps * TAPS_MULT;
|
||||
#if SINC_COEFF_LERP
|
||||
const float *delta_table = phase_table + taps;
|
||||
__m256 delta = _mm256_set1_ps((float)
|
||||
(resamp->time & SUBPHASE_MASK) * SUBPHASE_MOD);
|
||||
#endif
|
||||
|
||||
__m256 sum_l = _mm256_setzero_ps();
|
||||
__m256 sum_r = _mm256_setzero_ps();
|
||||
|
||||
for (i = 0; i < taps; i += 8)
|
||||
{
|
||||
__m256 buf_l = _mm256_loadu_ps(buffer_l + i);
|
||||
__m256 buf_r = _mm256_loadu_ps(buffer_r + i);
|
||||
|
||||
#if SINC_COEFF_LERP
|
||||
__m256 deltas = _mm256_load_ps(delta_table + i);
|
||||
__m256 sinc = _mm256_add_ps(_mm256_load_ps(phase_table + i),
|
||||
_mm256_mul_ps(deltas, delta));
|
||||
#else
|
||||
__m256 sinc = _mm256_load_ps(phase_table + i);
|
||||
#endif
|
||||
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_;
|
||||
|
||||
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;
|
||||
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 >> SUBPHASE_BITS;
|
||||
const float *phase_table = resamp->phase_table + phase * taps * TAPS_MULT;
|
||||
#if SINC_COEFF_LERP
|
||||
const float *delta_table = phase_table + taps;
|
||||
__m128 delta = _mm_set1_ps((float)
|
||||
(resamp->time & SUBPHASE_MASK) * SUBPHASE_MOD);
|
||||
#endif
|
||||
|
||||
__m128 sum_l = _mm_setzero_ps();
|
||||
__m128 sum_r = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < taps; i += 4)
|
||||
{
|
||||
__m128 buf_l = _mm_loadu_ps(buffer_l + i);
|
||||
__m128 buf_r = _mm_loadu_ps(buffer_r + i);
|
||||
|
||||
#if SINC_COEFF_LERP
|
||||
__m128 deltas = _mm_load_ps(delta_table + i);
|
||||
__m128 _sinc = _mm_add_ps(_mm_load_ps(phase_table + i),
|
||||
_mm_mul_ps(deltas, delta));
|
||||
#else
|
||||
__m128 _sinc = _mm_load_ps(phase_table + i);
|
||||
#endif
|
||||
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_;
|
||||
|
||||
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;
|
||||
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 >> SUBPHASE_BITS;
|
||||
const float *phase_table = resamp->phase_table + phase * taps * TAPS_MULT;
|
||||
#if SINC_COEFF_LERP
|
||||
const float *delta_table = phase_table + taps;
|
||||
float delta = (float)
|
||||
(resamp->time & SUBPHASE_MASK) * SUBPHASE_MOD;
|
||||
#endif
|
||||
float sum_l = 0.0f;
|
||||
float sum_r = 0.0f;
|
||||
|
||||
for (i = 0; i < taps; i++)
|
||||
{
|
||||
#if SINC_COEFF_LERP
|
||||
float sinc_val = phase_table[i] + delta_table[i] * delta;
|
||||
#else
|
||||
float sinc_val = phase_table[i];
|
||||
#endif
|
||||
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 sinc_init_table(rarch_sinc_resampler_t *resamp, double cutoff,
|
||||
float *phase_table, int phases, int taps, bool calculate_delta)
|
||||
{
|
||||
int i, j;
|
||||
double window_mod = 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) *
|
||||
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) *
|
||||
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_free(void *data)
|
||||
{
|
||||
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)data;
|
||||
if (resamp)
|
||||
memalign_free(resamp->main_buffer);
|
||||
free(resamp);
|
||||
}
|
||||
|
||||
static void *resampler_sinc_new(const struct resampler_config *config,
|
||||
double bandwidth_mod, resampler_simd_mask_t mask)
|
||||
{
|
||||
double cutoff;
|
||||
size_t phase_elems, elems;
|
||||
rarch_sinc_resampler_t *re = (rarch_sinc_resampler_t*)
|
||||
calloc(1, sizeof(*re));
|
||||
|
||||
if (!re)
|
||||
return NULL;
|
||||
|
||||
(void)config;
|
||||
|
||||
re->taps = TAPS;
|
||||
cutoff = CUTOFF;
|
||||
|
||||
/* 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__) && ENABLE_AVX) || (defined(__ARM_NEON__))
|
||||
re->taps = (re->taps + 7) & ~7;
|
||||
#else
|
||||
re->taps = (re->taps + 3) & ~3;
|
||||
#endif
|
||||
|
||||
phase_elems = ((1 << PHASE_BITS) * re->taps) * TAPS_MULT;
|
||||
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;
|
||||
|
||||
sinc_init_table(re, cutoff, re->phase_table,
|
||||
1 << PHASE_BITS, re->taps, SINC_COEFF_LERP);
|
||||
|
||||
sinc_resampler.process = resampler_sinc_process_c;
|
||||
|
||||
#if defined(__AVX__) && ENABLE_AVX
|
||||
if (mask & RESAMPLER_SIMD_AVX)
|
||||
sinc_resampler.process = resampler_sinc_process_avx;
|
||||
#elif defined(__SSE__)
|
||||
if (mask & RESAMPLER_SIMD_SSE)
|
||||
sinc_resampler.process = resampler_sinc_process_sse;
|
||||
#elif defined(__ARM_NEON__) && !defined(SINC_COEFF_LERP)
|
||||
if (mask & RESAMPLER_SIMD_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"
|
||||
};
|
@ -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
|
159
libretro-common/compat/compat_fnmatch.c
Normal file
159
libretro-common/compat/compat_fnmatch.c
Normal file
@ -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
|
219
libretro-common/compat/compat_getopt.c
Normal file
219
libretro-common/compat/compat_getopt.c
Normal file
@ -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 == 1)
|
||||
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 '?';
|
||||
}
|
619
libretro-common/compat/compat_ifaddrs.c
Normal file
619
libretro-common/compat/compat_ifaddrs.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
106
libretro-common/compat/compat_posix_string.c
Normal file
106
libretro-common/compat/compat_posix_string.c
Normal file
@ -0,0 +1,106 @@
|
||||
/* 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>
|
||||
|
||||
#include <retro_assert.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
|
58
libretro-common/compat/compat_snprintf.c
Normal file
58
libretro-common/compat/compat_snprintf.c
Normal file
@ -0,0 +1,58 @@
|
||||
/* 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 */
|
||||
|
||||
#include <retro_common.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* 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;
|
||||
}
|
59
libretro-common/compat/compat_strcasestr.c
Normal file
59
libretro-common/compat/compat_strcasestr.c
Normal file
@ -0,0 +1,59 @@
|
||||
/* 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>
|
||||
#include <retro_assert.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;
|
||||
}
|
64
libretro-common/compat/compat_strl.c
Normal file
64
libretro-common/compat/compat_strl.c
Normal file
@ -0,0 +1,64 @@
|
||||
/* 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 <ctype.h>
|
||||
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
|
||||
#include <retro_assert.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
|
10
libretro-common/crt/include/string.h
Normal file
10
libretro-common/crt/include/string.h
Normal file
@ -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
|
34
libretro-common/crt/string.c
Normal file
34
libretro-common/crt/string.c
Normal file
@ -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;
|
||||
}
|
150
libretro-common/dynamic/dylib.c
Normal file
150
libretro-common/dynamic/dylib.c
Normal file
@ -0,0 +1,150 @@
|
||||
/* 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>
|
||||
|
||||
#ifdef NEED_DYNAMIC
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <compat/posix_string.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#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);
|
||||
dylib_t lib = LoadLibrary(path);
|
||||
|
||||
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
|
89
libretro-common/encodings/encoding_crc32.c
Normal file
89
libretro-common/encodings/encoding_crc32.c
Normal file
@ -0,0 +1,89 @@
|
||||
/* 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>
|
||||
|
||||
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;
|
||||
}
|
260
libretro-common/encodings/encoding_utf.c
Normal file
260
libretro-common/encodings/encoding_utf.c
Normal file
@ -0,0 +1,260 @@
|
||||
/* 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>
|
||||
|
||||
static INLINE 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;
|
||||
|
||||
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;
|
||||
while (*string)
|
||||
{
|
||||
if ((*string & 0xC0) != 0x80)
|
||||
ret++;
|
||||
string++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static INLINE 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;
|
||||
|
||||
if (first<128)
|
||||
return first;
|
||||
|
||||
ret = 0;
|
||||
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;
|
||||
}
|
773
libretro-common/features/features_cpu.c
Normal file
773
libretro-common/features/features_cpu.c
Normal file
@ -0,0 +1,773 @@
|
||||
/* 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>
|
||||
|
||||
#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 <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
|
||||
|
||||
#if defined(_3DS)
|
||||
#include <3ds/svc.h>
|
||||
#include <3ds/os.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;
|
||||
static const unsigned __int64 epoch = 11644473600000000ULL;
|
||||
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());
|
||||
#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__)
|
||||
#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", RFILE_MODE_READ_TEXT, -1);
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
897
libretro-common/file/archive_file.c
Normal file
897
libretro-common/file/archive_file.c
Normal file
@ -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 = NULL;
|
||||
|
||||
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 = NULL;
|
||||
|
||||
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 && path_file_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;
|
||||
}
|
471
libretro-common/file/archive_file_7z.c
Normal file
471
libretro-common/file/archive_file_7z.c
Normal file
@ -0,0 +1,471 @@
|
||||
/* 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 <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 "../../deps/7zip/7z.h"
|
||||
#include "../../deps/7zip/7zCrc.h"
|
||||
#include "../../deps/7zip/7zFile.h"
|
||||
|
||||
#define SEVENZIP_MAGIC "7z\xBC\xAF\x27\x1C"
|
||||
#define SEVENZIP_MAGIC_LEN 6
|
||||
|
||||
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;
|
||||
|
||||
/* Could not open 7zip archive? */
|
||||
if (InFile_Open(&archiveStream.file, path))
|
||||
return -1;
|
||||
|
||||
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;
|
||||
|
||||
/*RARCH_LOG_OUTPUT("Opened archive %s. Now trying to extract %s\n",
|
||||
path, needle);*/
|
||||
|
||||
/* 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))
|
||||
{
|
||||
/*RARCH_ERR("Could not open outfilepath %s.\n",
|
||||
optional_outfile);*/
|
||||
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;
|
||||
|
||||
/* could not open 7zip archive? */
|
||||
if (InFile_Open(&sevenzip_context->archiveStream.file, file))
|
||||
goto error;
|
||||
|
||||
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"
|
||||
};
|
378
libretro-common/file/archive_file_zlib.c
Normal file
378
libretro-common/file/archive_file_zlib.c
Normal file
@ -0,0 +1,378 @@
|
||||
/* 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 <file/archive_file.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <streams/trans_stream.h>
|
||||
#include <string.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 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 0
|
||||
RARCH_LOG("[deflate] Path: %s, CRC32: 0x%x\n", name, crc32);
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
/*RARCH_LOG("%s: %s\n",
|
||||
msg_hash_to_str(MSG_EXTRACTING_FILE),
|
||||
userdata->decomp_state.opt_file);*/
|
||||
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"
|
||||
};
|
978
libretro-common/file/config_file.c
Normal file
978
libretro-common/file/config_file.c
Normal file
@ -0,0 +1,978 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (config_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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if !defined(_WIN32) && !defined(__CELLOS_LV2__) && !defined(_XBOX)
|
||||
#include <sys/param.h> /* PATH_MAX */
|
||||
#elif defined(_WIN32) && !defined(_XBOX)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#elif defined(_XBOX)
|
||||
#include <xtl.h>
|
||||
#endif
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
#include <compat/msvc.h>
|
||||
#include <file/config_file.h>
|
||||
#include <file/file_path.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <rhash.h>
|
||||
|
||||
#define MAX_INCLUDE_DEPTH 16
|
||||
|
||||
struct config_entry_list
|
||||
{
|
||||
/* If we got this from an #include,
|
||||
* do not allow overwrite. */
|
||||
bool readonly;
|
||||
char *key;
|
||||
char *value;
|
||||
uint32_t key_hash;
|
||||
|
||||
struct config_entry_list *next;
|
||||
};
|
||||
|
||||
struct config_include_list
|
||||
{
|
||||
char *path;
|
||||
struct config_include_list *next;
|
||||
};
|
||||
|
||||
struct config_file
|
||||
{
|
||||
char *path;
|
||||
struct config_entry_list *entries;
|
||||
struct config_entry_list *tail;
|
||||
unsigned include_depth;
|
||||
|
||||
struct config_include_list *includes;
|
||||
};
|
||||
|
||||
static config_file_t *config_file_new_internal(
|
||||
const char *path, unsigned depth);
|
||||
|
||||
static char *getaline(FILE *file)
|
||||
{
|
||||
char* newline = (char*)malloc(9);
|
||||
char* newline_tmp = NULL;
|
||||
size_t cur_size = 8;
|
||||
size_t idx = 0;
|
||||
int in = getc(file);
|
||||
|
||||
if (!newline)
|
||||
return NULL;
|
||||
|
||||
while (in != EOF && in != '\n')
|
||||
{
|
||||
if (idx == cur_size)
|
||||
{
|
||||
cur_size *= 2;
|
||||
newline_tmp = (char*)realloc(newline, cur_size + 1);
|
||||
|
||||
if (!newline_tmp)
|
||||
{
|
||||
free(newline);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newline = newline_tmp;
|
||||
}
|
||||
|
||||
newline[idx++] = in;
|
||||
in = getc(file);
|
||||
}
|
||||
newline[idx] = '\0';
|
||||
return newline;
|
||||
}
|
||||
|
||||
static char *strip_comment(char *str)
|
||||
{
|
||||
/* Remove everything after comment.
|
||||
* Keep #s inside string literals. */
|
||||
char *string_end = str + strlen(str);
|
||||
bool cut_comment = true;
|
||||
|
||||
while (!string_is_empty(str))
|
||||
{
|
||||
char *comment = NULL;
|
||||
char *literal = strchr(str, '\"');
|
||||
if (!literal)
|
||||
literal = string_end;
|
||||
comment = (char*)strchr(str, '#');
|
||||
if (!comment)
|
||||
comment = string_end;
|
||||
|
||||
if (cut_comment && literal < comment)
|
||||
{
|
||||
cut_comment = false;
|
||||
str = literal + 1;
|
||||
}
|
||||
else if (!cut_comment && literal)
|
||||
{
|
||||
cut_comment = true;
|
||||
str = literal + 1;
|
||||
}
|
||||
else if (comment)
|
||||
{
|
||||
*comment = '\0';
|
||||
str = comment;
|
||||
}
|
||||
else
|
||||
str = string_end;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *extract_value(char *line, bool is_value)
|
||||
{
|
||||
char *save = NULL;
|
||||
char *tok = NULL;
|
||||
|
||||
if (is_value)
|
||||
{
|
||||
while (isspace((int)*line))
|
||||
line++;
|
||||
|
||||
/* If we don't have an equal sign here,
|
||||
* we've got an invalid string. */
|
||||
if (*line != '=')
|
||||
return NULL;
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
while (isspace((int)*line))
|
||||
line++;
|
||||
|
||||
/* We have a full string. Read until next ". */
|
||||
if (*line == '"')
|
||||
{
|
||||
line++;
|
||||
tok = strtok_r(line, "\"", &save);
|
||||
if (!tok)
|
||||
return NULL;
|
||||
return strdup(tok);
|
||||
}
|
||||
else if (*line == '\0') /* Nothing */
|
||||
return NULL;
|
||||
|
||||
/* We don't have that. Read until next space. */
|
||||
tok = strtok_r(line, " \n\t\f\r\v", &save);
|
||||
if (tok)
|
||||
return strdup(tok);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_include_list(config_file_t *conf, const char *path)
|
||||
{
|
||||
struct config_include_list *head = conf->includes;
|
||||
struct config_include_list *node = (struct config_include_list*)calloc(1, sizeof(*node));
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
node->path = strdup(path);
|
||||
|
||||
if (head)
|
||||
{
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
|
||||
head->next = node;
|
||||
}
|
||||
else
|
||||
conf->includes = node;
|
||||
}
|
||||
|
||||
static void set_list_readonly(struct config_entry_list *list)
|
||||
{
|
||||
while (list)
|
||||
{
|
||||
list->readonly = true;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move semantics? */
|
||||
static void add_child_list(config_file_t *parent, config_file_t *child)
|
||||
{
|
||||
if (parent->entries)
|
||||
{
|
||||
struct config_entry_list *head = parent->entries;
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
|
||||
set_list_readonly(child->entries);
|
||||
head->next = child->entries;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_list_readonly(child->entries);
|
||||
parent->entries = child->entries;
|
||||
}
|
||||
|
||||
child->entries = NULL;
|
||||
|
||||
/* Rebase tail. */
|
||||
if (parent->entries)
|
||||
{
|
||||
struct config_entry_list *head =
|
||||
(struct config_entry_list*)parent->entries;
|
||||
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
parent->tail = head;
|
||||
}
|
||||
else
|
||||
parent->tail = NULL;
|
||||
}
|
||||
|
||||
static void add_sub_conf(config_file_t *conf, char *line)
|
||||
{
|
||||
char real_path[PATH_MAX_LENGTH];
|
||||
config_file_t *sub_conf = NULL;
|
||||
char *path = extract_value(line, false);
|
||||
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
add_include_list(conf, path);
|
||||
|
||||
real_path[0] = '\0';
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!string_is_empty(conf->path))
|
||||
fill_pathname_resolve_relative(real_path, conf->path,
|
||||
path, sizeof(real_path));
|
||||
#else
|
||||
#ifndef __CELLOS_LV2__
|
||||
if (*path == '~')
|
||||
{
|
||||
const char *home = getenv("HOME");
|
||||
strlcpy(real_path, home ? home : "", sizeof(real_path));
|
||||
strlcat(real_path, path + 1, sizeof(real_path));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (!string_is_empty(conf->path))
|
||||
fill_pathname_resolve_relative(real_path, conf->path,
|
||||
path, sizeof(real_path));
|
||||
#endif
|
||||
|
||||
sub_conf = (config_file_t*)
|
||||
config_file_new_internal(real_path, conf->include_depth + 1);
|
||||
if (!sub_conf)
|
||||
{
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Pilfer internal list. */
|
||||
add_child_list(conf, sub_conf);
|
||||
config_file_free(sub_conf);
|
||||
free(path);
|
||||
}
|
||||
|
||||
static bool parse_line(config_file_t *conf,
|
||||
struct config_entry_list *list, char *line)
|
||||
{
|
||||
char *comment = NULL;
|
||||
char *key_tmp = NULL;
|
||||
size_t cur_size = 8;
|
||||
size_t idx = 0;
|
||||
char *key = (char*)malloc(9);
|
||||
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
if (!line || !*line)
|
||||
{
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
comment = strip_comment(line);
|
||||
|
||||
/* Starting line with # and include includes config files. */
|
||||
if ((comment == line) && (conf->include_depth < MAX_INCLUDE_DEPTH))
|
||||
{
|
||||
comment++;
|
||||
if (strstr(comment, "include ") == comment)
|
||||
{
|
||||
add_sub_conf(conf, comment + strlen("include "));
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (conf->include_depth >= MAX_INCLUDE_DEPTH)
|
||||
{
|
||||
fprintf(stderr, "!!! #include depth exceeded for config. Might be a cycle.\n");
|
||||
}
|
||||
|
||||
/* Skips to first character. */
|
||||
while (isspace((int)*line))
|
||||
line++;
|
||||
|
||||
while (isgraph((int)*line))
|
||||
{
|
||||
if (idx == cur_size)
|
||||
{
|
||||
cur_size *= 2;
|
||||
key_tmp = (char*)realloc(key, cur_size + 1);
|
||||
|
||||
if (!key_tmp)
|
||||
{
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
key = key_tmp;
|
||||
}
|
||||
|
||||
key[idx++] = *line++;
|
||||
}
|
||||
key[idx] = '\0';
|
||||
list->key = key;
|
||||
list->key_hash = djb2_calculate(key);
|
||||
|
||||
list->value = extract_value(line, true);
|
||||
if (!list->value)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static config_file_t *config_file_new_internal(
|
||||
const char *path, unsigned depth)
|
||||
{
|
||||
FILE *file = NULL;
|
||||
struct config_file *conf = (struct config_file*)calloc(1, sizeof(*conf));
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
if (!path || !*path)
|
||||
return conf;
|
||||
|
||||
if (path_is_directory(path))
|
||||
goto error;
|
||||
|
||||
conf->path = strdup(path);
|
||||
if (!conf->path)
|
||||
goto error;
|
||||
|
||||
conf->include_depth = depth;
|
||||
file = fopen(path, "r");
|
||||
|
||||
if (!file)
|
||||
{
|
||||
free(conf->path);
|
||||
goto error;
|
||||
}
|
||||
setvbuf(file, NULL, _IOFBF, 0x4000);
|
||||
|
||||
while (!feof(file))
|
||||
{
|
||||
struct config_entry_list *list = (struct config_entry_list*)
|
||||
calloc(1, sizeof(*list));
|
||||
char *line = NULL;
|
||||
|
||||
if (!list)
|
||||
{
|
||||
config_file_free(conf);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
line = getaline(file);
|
||||
|
||||
if (!line)
|
||||
{
|
||||
free(list);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parse_line(conf, list, line))
|
||||
{
|
||||
if (conf->entries)
|
||||
conf->tail->next = list;
|
||||
else
|
||||
conf->entries = list;
|
||||
|
||||
conf->tail = list;
|
||||
}
|
||||
|
||||
free(line);
|
||||
|
||||
if (list != conf->tail)
|
||||
free(list);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return conf;
|
||||
|
||||
error:
|
||||
free(conf);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void config_file_free(config_file_t *conf)
|
||||
{
|
||||
struct config_include_list *inc_tmp = NULL;
|
||||
struct config_entry_list *tmp = NULL;
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
tmp = conf->entries;
|
||||
while (tmp)
|
||||
{
|
||||
struct config_entry_list *hold = NULL;
|
||||
if (tmp->key)
|
||||
free(tmp->key);
|
||||
if (tmp->value)
|
||||
free(tmp->value);
|
||||
|
||||
tmp->value = NULL;
|
||||
tmp->key = NULL;
|
||||
|
||||
hold = tmp;
|
||||
tmp = tmp->next;
|
||||
|
||||
if (hold)
|
||||
free(hold);
|
||||
}
|
||||
|
||||
inc_tmp = (struct config_include_list*)conf->includes;
|
||||
while (inc_tmp)
|
||||
{
|
||||
struct config_include_list *hold = NULL;
|
||||
free(inc_tmp->path);
|
||||
hold = (struct config_include_list*)inc_tmp;
|
||||
inc_tmp = inc_tmp->next;
|
||||
free(hold);
|
||||
}
|
||||
|
||||
if (conf->path)
|
||||
free(conf->path);
|
||||
free(conf);
|
||||
}
|
||||
|
||||
bool config_append_file(config_file_t *conf, const char *path)
|
||||
{
|
||||
config_file_t *new_conf = config_file_new(path);
|
||||
if (!new_conf)
|
||||
return false;
|
||||
|
||||
if (new_conf->tail)
|
||||
{
|
||||
new_conf->tail->next = conf->entries;
|
||||
conf->entries = new_conf->entries; /* Pilfer. */
|
||||
new_conf->entries = NULL;
|
||||
}
|
||||
|
||||
config_file_free(new_conf);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
config_file_t *config_file_new_from_string(const char *from_string)
|
||||
{
|
||||
size_t i;
|
||||
struct string_list *lines = NULL;
|
||||
struct config_file *conf = (struct config_file*)calloc(1, sizeof(*conf));
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
if (!from_string)
|
||||
return conf;
|
||||
|
||||
conf->path = NULL;
|
||||
conf->include_depth = 0;
|
||||
|
||||
lines = string_split(from_string, "\n");
|
||||
if (!lines)
|
||||
return conf;
|
||||
|
||||
for (i = 0; i < lines->size; i++)
|
||||
{
|
||||
struct config_entry_list *list = (struct config_entry_list*)
|
||||
calloc(1, sizeof(*list));
|
||||
char* line = lines->elems[i].data;
|
||||
|
||||
if (!list)
|
||||
{
|
||||
string_list_free(lines);
|
||||
config_file_free(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (line && conf)
|
||||
{
|
||||
if (parse_line(conf, list, line))
|
||||
{
|
||||
if (conf->entries)
|
||||
conf->tail->next = list;
|
||||
else
|
||||
conf->entries = list;
|
||||
|
||||
conf->tail = list;
|
||||
}
|
||||
}
|
||||
|
||||
if (list != conf->tail)
|
||||
free(list);
|
||||
}
|
||||
|
||||
string_list_free(lines);
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
config_file_t *config_file_new(const char *path)
|
||||
{
|
||||
return config_file_new_internal(path, 0);
|
||||
}
|
||||
|
||||
|
||||
static struct config_entry_list *config_get_entry(const config_file_t *conf,
|
||||
const char *key, struct config_entry_list **prev)
|
||||
{
|
||||
struct config_entry_list *entry;
|
||||
struct config_entry_list *previous = NULL;
|
||||
|
||||
uint32_t hash = djb2_calculate(key);
|
||||
|
||||
if (prev)
|
||||
previous = *prev;
|
||||
|
||||
for (entry = conf->entries; entry; entry = entry->next)
|
||||
{
|
||||
if (hash == entry->key_hash && string_is_equal(key, entry->key))
|
||||
return entry;
|
||||
|
||||
previous = entry;
|
||||
}
|
||||
|
||||
if (prev)
|
||||
*prev = previous;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool config_get_double(config_file_t *conf, const char *key, double *in)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
|
||||
if (entry)
|
||||
*in = strtod(entry->value, NULL);
|
||||
|
||||
return entry != NULL;
|
||||
}
|
||||
|
||||
bool config_get_float(config_file_t *conf, const char *key, float *in)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
/* strtof() is C99/POSIX. Just use the more portable kind. */
|
||||
*in = (float)strtod(entry->value, NULL);
|
||||
}
|
||||
|
||||
return entry != NULL;
|
||||
}
|
||||
|
||||
bool config_get_int(config_file_t *conf, const char *key, int *in)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
errno = 0;
|
||||
|
||||
if (entry)
|
||||
{
|
||||
int val = (int)strtol(entry->value, NULL, 0);
|
||||
|
||||
if (errno == 0)
|
||||
*in = val;
|
||||
}
|
||||
|
||||
return entry != NULL && errno == 0;
|
||||
}
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L
|
||||
bool config_get_uint64(config_file_t *conf, const char *key, uint64_t *in)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
errno = 0;
|
||||
|
||||
if (entry)
|
||||
{
|
||||
uint64_t val = strtoull(entry->value, NULL, 0);
|
||||
|
||||
if (errno == 0)
|
||||
*in = val;
|
||||
}
|
||||
|
||||
return entry != NULL && errno == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool config_get_uint(config_file_t *conf, const char *key, unsigned *in)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
errno = 0;
|
||||
|
||||
if (entry)
|
||||
{
|
||||
unsigned val = (unsigned)strtoul(entry->value, NULL, 0);
|
||||
|
||||
if (errno == 0)
|
||||
*in = val;
|
||||
}
|
||||
|
||||
return entry != NULL && errno == 0;
|
||||
}
|
||||
|
||||
bool config_get_hex(config_file_t *conf, const char *key, unsigned *in)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
errno = 0;
|
||||
|
||||
if (entry)
|
||||
{
|
||||
unsigned val = (unsigned)strtoul(entry->value, NULL, 16);
|
||||
|
||||
if (errno == 0)
|
||||
*in = val;
|
||||
}
|
||||
|
||||
return entry != NULL && errno == 0;
|
||||
}
|
||||
|
||||
bool config_get_char(config_file_t *conf, const char *key, char *in)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
if (entry->value[0] && entry->value[1])
|
||||
return false;
|
||||
|
||||
*in = *entry->value;
|
||||
}
|
||||
|
||||
return entry != NULL;
|
||||
}
|
||||
|
||||
bool config_get_string(config_file_t *conf, const char *key, char **str)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
|
||||
if (entry)
|
||||
*str = strdup(entry->value);
|
||||
|
||||
return entry != NULL;
|
||||
}
|
||||
|
||||
bool config_get_config_path(config_file_t *conf, char *s, size_t len)
|
||||
{
|
||||
if (!conf)
|
||||
return false;
|
||||
|
||||
return strlcpy(s, conf->path, len);
|
||||
}
|
||||
|
||||
bool config_get_array(config_file_t *conf, const char *key,
|
||||
char *buf, size_t size)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
|
||||
if (entry)
|
||||
return strlcpy(buf, entry->value, size) < size;
|
||||
|
||||
return entry != NULL;
|
||||
}
|
||||
|
||||
bool config_get_path(config_file_t *conf, const char *key,
|
||||
char *buf, size_t size)
|
||||
{
|
||||
#if defined(RARCH_CONSOLE)
|
||||
return config_get_array(conf, key, buf, size);
|
||||
#else
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
|
||||
if (entry)
|
||||
fill_pathname_expand_special(buf, entry->value, size);
|
||||
|
||||
return entry != NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool config_get_bool(config_file_t *conf, const char *key, bool *in)
|
||||
{
|
||||
const struct config_entry_list *entry = config_get_entry(conf, key, NULL);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
if (string_is_equal_fast(entry->value, "true", 4))
|
||||
*in = true;
|
||||
else if (string_is_equal_fast(entry->value, "1", 1))
|
||||
*in = true;
|
||||
else if (string_is_equal_fast(entry->value, "false", 5))
|
||||
*in = false;
|
||||
else if (string_is_equal_fast(entry->value, "0", 1))
|
||||
*in = false;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return entry != NULL;
|
||||
}
|
||||
|
||||
void config_set_string(config_file_t *conf, const char *key, const char *val)
|
||||
{
|
||||
struct config_entry_list *last = conf->entries;
|
||||
struct config_entry_list *entry = config_get_entry(conf, key, &last);
|
||||
|
||||
if (entry && !entry->readonly)
|
||||
{
|
||||
free(entry->value);
|
||||
entry->value = strdup(val);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!val) return;
|
||||
|
||||
entry = (struct config_entry_list*)calloc(1, sizeof(*entry));
|
||||
if (!entry) return;
|
||||
|
||||
entry->key = strdup(key);
|
||||
entry->value = strdup(val);
|
||||
|
||||
if (last)
|
||||
last->next = entry;
|
||||
else
|
||||
conf->entries = entry;
|
||||
}
|
||||
|
||||
void config_unset(config_file_t *conf, const char *key)
|
||||
{
|
||||
struct config_entry_list *last = conf->entries;
|
||||
struct config_entry_list *entry = config_get_entry(conf, key, &last);
|
||||
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
entry->key = NULL;
|
||||
entry->value = NULL;
|
||||
free(entry->key);
|
||||
free(entry->value);
|
||||
}
|
||||
|
||||
void config_set_path(config_file_t *conf, const char *entry, const char *val)
|
||||
{
|
||||
#if defined(RARCH_CONSOLE)
|
||||
config_set_string(conf, entry, val);
|
||||
#else
|
||||
char buf[PATH_MAX_LENGTH];
|
||||
|
||||
buf[0] = '\0';
|
||||
fill_pathname_abbreviate_special(buf, val, sizeof(buf));
|
||||
config_set_string(conf, entry, buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
void config_set_double(config_file_t *conf, const char *key, double val)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
buf[0] = '\0';
|
||||
#ifdef __cplusplus
|
||||
snprintf(buf, sizeof(buf), "%f", (float)val);
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L
|
||||
snprintf(buf, sizeof(buf), "%lf", val);
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "%f", (float)val);
|
||||
#endif
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_float(config_file_t *conf, const char *key, float val)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
buf[0] = '\0';
|
||||
snprintf(buf, sizeof(buf), "%f", val);
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_int(config_file_t *conf, const char *key, int val)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
buf[0] = '\0';
|
||||
snprintf(buf, sizeof(buf), "%d", val);
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_hex(config_file_t *conf, const char *key, unsigned val)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
buf[0] = '\0';
|
||||
snprintf(buf, sizeof(buf), "%x", val);
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_uint64(config_file_t *conf, const char *key, uint64_t val)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
buf[0] = '\0';
|
||||
#ifdef _WIN32
|
||||
snprintf(buf, sizeof(buf), "%I64u", val);
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "%llu", (long long unsigned)val);
|
||||
#endif
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_char(config_file_t *conf, const char *key, char val)
|
||||
{
|
||||
char buf[2];
|
||||
|
||||
buf[0] = '\0';
|
||||
snprintf(buf, sizeof(buf), "%c", val);
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_bool(config_file_t *conf, const char *key, bool val)
|
||||
{
|
||||
config_set_string(conf, key, val ? "true" : "false");
|
||||
}
|
||||
|
||||
bool config_file_write(config_file_t *conf, const char *path)
|
||||
{
|
||||
FILE *file;
|
||||
|
||||
if (path)
|
||||
{
|
||||
file = fopen(path, "w");
|
||||
if (!file)
|
||||
return false;
|
||||
setvbuf(file, NULL, _IOFBF, 0x4000);
|
||||
}
|
||||
else
|
||||
file = stdout;
|
||||
|
||||
config_file_dump(conf, file);
|
||||
|
||||
if (path)
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void config_file_dump(config_file_t *conf, FILE *file)
|
||||
{
|
||||
struct config_entry_list *list = NULL;
|
||||
struct config_include_list *includes = conf->includes;
|
||||
|
||||
while (includes)
|
||||
{
|
||||
fprintf(file, "#include \"%s\"\n", includes->path);
|
||||
includes = includes->next;
|
||||
}
|
||||
|
||||
list = (struct config_entry_list*)conf->entries;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (!list->readonly && list->key)
|
||||
fprintf(file, "%s = \"%s\"\n", list->key, list->value);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_entry_exists(config_file_t *conf, const char *entry)
|
||||
{
|
||||
struct config_entry_list *list = conf->entries;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (string_is_equal(entry, list->key))
|
||||
return true;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_entry_list_head(config_file_t *conf,
|
||||
struct config_file_entry *entry)
|
||||
{
|
||||
const struct config_entry_list *head = conf->entries;
|
||||
|
||||
if (!head)
|
||||
return false;
|
||||
|
||||
entry->key = head->key;
|
||||
entry->value = head->value;
|
||||
entry->next = head->next;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool config_get_entry_list_next(struct config_file_entry *entry)
|
||||
{
|
||||
const struct config_entry_list *next = entry->next;
|
||||
|
||||
if (!next)
|
||||
return false;
|
||||
|
||||
entry->key = next->key;
|
||||
entry->value = next->value;
|
||||
entry->next = next->next;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool config_file_exists(const char *path)
|
||||
{
|
||||
config_file_t *config = config_file_new(path);
|
||||
if (!config)
|
||||
return false;
|
||||
|
||||
config_file_free(config);
|
||||
return true;
|
||||
}
|
149
libretro-common/file/config_file_userdata.c
Normal file
149
libretro-common/file/config_file_userdata.c
Normal file
@ -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);
|
||||
}
|
896
libretro-common/file/file_path.c
Normal file
896
libretro-common/file/file_path.c
Normal file
@ -0,0 +1,896 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (file_path.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 <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <file/file_path.h>
|
||||
|
||||
#ifndef __MACH__
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
#endif
|
||||
#include <compat/strcasestr.h>
|
||||
#include <retro_assert.h>
|
||||
#include <retro_miscellaneous.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)
|
||||
#define SCE_ERROR_ERRNO_EEXIST 0x80010011
|
||||
#include <psp2/io/fcntl.h>
|
||||
#include <psp2/io/dirent.h>
|
||||
#include <psp2/io/stat.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(PSP)
|
||||
#include <pspkernel.h>
|
||||
#endif
|
||||
|
||||
#ifdef __HAIKU__
|
||||
#include <kernel/image.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#include <cell/cell_fs.h>
|
||||
#endif
|
||||
|
||||
#if defined(VITA)
|
||||
#define FIO_S_ISDIR SCE_S_ISDIR
|
||||
#endif
|
||||
|
||||
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
|
||||
#include <unistd.h> /* stat() is defined here */
|
||||
#endif
|
||||
|
||||
enum stat_mode
|
||||
{
|
||||
IS_DIRECTORY = 0,
|
||||
IS_CHARACTER_SPECIAL,
|
||||
IS_VALID
|
||||
};
|
||||
|
||||
static bool path_stat(const char *path, enum stat_mode mode, int32_t *size)
|
||||
{
|
||||
#if defined(VITA) || defined(PSP)
|
||||
SceIoStat buf;
|
||||
char *tmp = strdup(path);
|
||||
size_t len = strlen(tmp);
|
||||
if (tmp[len-1] == '/')
|
||||
tmp[len-1]='\0';
|
||||
|
||||
if (sceIoGetstat(tmp, &buf) < 0)
|
||||
{
|
||||
free(tmp);
|
||||
return false;
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
CellFsStat buf;
|
||||
if (cellFsStat(path, &buf) < 0)
|
||||
return false;
|
||||
#elif defined(_WIN32)
|
||||
struct _stat buf;
|
||||
DWORD file_info = GetFileAttributes(path);
|
||||
|
||||
_stat(path, &buf);
|
||||
|
||||
if (file_info == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
#else
|
||||
struct stat buf;
|
||||
if (stat(path, &buf) < 0)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (size)
|
||||
*size = (int32_t)buf.st_size;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case IS_DIRECTORY:
|
||||
#if defined(VITA) || defined(PSP)
|
||||
return FIO_S_ISDIR(buf.st_mode);
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
return ((buf.st_mode & S_IFMT) == S_IFDIR);
|
||||
#elif defined(_WIN32)
|
||||
return (file_info & FILE_ATTRIBUTE_DIRECTORY);
|
||||
#else
|
||||
return S_ISDIR(buf.st_mode);
|
||||
#endif
|
||||
case IS_CHARACTER_SPECIAL:
|
||||
#if defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) || defined(_WIN32)
|
||||
return false;
|
||||
#else
|
||||
return S_ISCHR(buf.st_mode);
|
||||
#endif
|
||||
case IS_VALID:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_is_directory:
|
||||
* @path : path
|
||||
*
|
||||
* Checks if path is a directory.
|
||||
*
|
||||
* Returns: true (1) if path is a directory, otherwise false (0).
|
||||
*/
|
||||
bool path_is_directory(const char *path)
|
||||
{
|
||||
return path_stat(path, IS_DIRECTORY, NULL);
|
||||
}
|
||||
|
||||
bool path_is_character_special(const char *path)
|
||||
{
|
||||
return path_stat(path, IS_CHARACTER_SPECIAL, NULL);
|
||||
}
|
||||
|
||||
bool path_is_valid(const char *path)
|
||||
{
|
||||
return path_stat(path, IS_VALID, NULL);
|
||||
}
|
||||
|
||||
int32_t path_get_size(const char *path)
|
||||
{
|
||||
int32_t filesize = 0;
|
||||
if (path_stat(path, IS_VALID, &filesize))
|
||||
return filesize;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_mkdir_norecurse:
|
||||
* @dir : directory
|
||||
*
|
||||
* Create directory on filesystem.
|
||||
*
|
||||
* Returns: true (1) if directory could be created, otherwise false (0).
|
||||
**/
|
||||
static bool mkdir_norecurse(const char *dir)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
int ret = _mkdir(dir);
|
||||
#elif defined(IOS)
|
||||
int ret = mkdir(dir, 0755);
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
int ret = sceIoMkdir(dir, 0777);
|
||||
#elif defined(__QNX__)
|
||||
int ret = mkdir(dir, 0777);
|
||||
#else
|
||||
int ret = mkdir(dir, 0750);
|
||||
#endif
|
||||
|
||||
/* Don't treat this as an error. */
|
||||
#if defined(VITA)
|
||||
if ((ret == SCE_ERROR_ERRNO_EEXIST) && path_is_directory(dir))
|
||||
ret = 0;
|
||||
#elif defined(PSP) || defined(_3DS) || defined(WIIU)
|
||||
if ((ret == -1) && path_is_directory(dir))
|
||||
ret = 0;
|
||||
#else
|
||||
if (ret < 0 && errno == EEXIST && path_is_directory(dir))
|
||||
ret = 0;
|
||||
#endif
|
||||
if (ret < 0)
|
||||
printf("mkdir(%s) error: %s.\n", dir, strerror(errno));
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_mkdir:
|
||||
* @dir : directory
|
||||
*
|
||||
* Create directory on filesystem.
|
||||
*
|
||||
* Returns: true (1) if directory could be created, otherwise false (0).
|
||||
**/
|
||||
bool path_mkdir(const char *dir)
|
||||
{
|
||||
const char *target = NULL;
|
||||
/* Use heap. Real chance of stack overflow if we recurse too hard. */
|
||||
char *basedir = strdup(dir);
|
||||
bool ret = false;
|
||||
|
||||
if (!basedir)
|
||||
return false;
|
||||
|
||||
path_parent_dir(basedir);
|
||||
if (!*basedir || string_is_equal(basedir, dir))
|
||||
goto end;
|
||||
|
||||
if (path_is_directory(basedir))
|
||||
{
|
||||
target = dir;
|
||||
ret = mkdir_norecurse(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
target = basedir;
|
||||
ret = path_mkdir(basedir);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
target = dir;
|
||||
ret = mkdir_norecurse(dir);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (target && !ret)
|
||||
printf("Failed to create directory: \"%s\".\n", target);
|
||||
free(basedir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_get_archive_delim:
|
||||
* @path : path
|
||||
*
|
||||
* Find delimiter of an archive file. Only the first '#'
|
||||
* after a compression extension is considered.
|
||||
*
|
||||
* Returns: pointer to the delimiter in the path if it contains
|
||||
* a path inside a compressed file, otherwise NULL.
|
||||
*/
|
||||
const char *path_get_archive_delim(const char *path)
|
||||
{
|
||||
const char *last = find_last_slash(path);
|
||||
const char *delim = NULL;
|
||||
|
||||
if (last)
|
||||
{
|
||||
delim = strcasestr(last, ".zip#");
|
||||
|
||||
if (!delim)
|
||||
delim = strcasestr(last, ".apk#");
|
||||
}
|
||||
|
||||
if (delim)
|
||||
return delim + 4;
|
||||
|
||||
if (last)
|
||||
delim = strcasestr(last, ".7z#");
|
||||
|
||||
if (delim)
|
||||
return delim + 3;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_get_extension:
|
||||
* @path : path
|
||||
*
|
||||
* Gets extension of file. Only '.'s
|
||||
* after the last slash are considered.
|
||||
*
|
||||
* Returns: extension part from the path.
|
||||
*/
|
||||
const char *path_get_extension(const char *path)
|
||||
{
|
||||
const char *ext = strrchr(path_basename(path), '.');
|
||||
if (!ext)
|
||||
return "";
|
||||
return ext + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_remove_extension:
|
||||
* @path : path
|
||||
*
|
||||
* Removes the extension from the path and returns the result.
|
||||
* Removes all text after and including the last '.'.
|
||||
* Only '.'s after the last slash are considered.
|
||||
*
|
||||
* Returns: path with the extension part removed.
|
||||
*/
|
||||
char *path_remove_extension(char *path)
|
||||
{
|
||||
char *last = (char*)strrchr(path_basename(path), '.');
|
||||
if (!last)
|
||||
return NULL;
|
||||
if (*last)
|
||||
*last = '\0';
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_is_compressed_file:
|
||||
* @path : path
|
||||
*
|
||||
* Checks if path is a compressed file.
|
||||
*
|
||||
* Returns: true (1) if path is a compressed file, otherwise false (0).
|
||||
**/
|
||||
bool path_is_compressed_file(const char* path)
|
||||
{
|
||||
const char *ext = path_get_extension(path);
|
||||
|
||||
if ( string_is_equal_noncase(ext, "zip")
|
||||
|| string_is_equal_noncase(ext, "apk")
|
||||
|| string_is_equal_noncase(ext, "7z"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_file_exists:
|
||||
* @path : path
|
||||
*
|
||||
* Checks if a file already exists at the specified path (@path).
|
||||
*
|
||||
* Returns: true (1) if file already exists, otherwise false (0).
|
||||
*/
|
||||
bool path_file_exists(const char *path)
|
||||
{
|
||||
FILE *dummy = fopen(path, "rb");
|
||||
|
||||
if (!dummy)
|
||||
return false;
|
||||
|
||||
fclose(dummy);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname:
|
||||
* @out_path : output path
|
||||
* @in_path : input path
|
||||
* @replace : what to replace
|
||||
* @size : buffer size of output path
|
||||
*
|
||||
* FIXME: Verify
|
||||
*
|
||||
* Replaces filename extension with 'replace' and outputs result to out_path.
|
||||
* The extension here is considered to be the string from the last '.'
|
||||
* to the end.
|
||||
*
|
||||
* Only '.'s after the last slash are considered as extensions.
|
||||
* If no '.' is present, in_path and replace will simply be concatenated.
|
||||
* 'size' is buffer size of 'out_path'.
|
||||
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" =>
|
||||
* out_path = "/foo/bar/baz/boo.asm"
|
||||
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
|
||||
* out_path = "/foo/bar/baz/boo"
|
||||
*/
|
||||
void fill_pathname(char *out_path, const char *in_path,
|
||||
const char *replace, size_t size)
|
||||
{
|
||||
char tmp_path[PATH_MAX_LENGTH];
|
||||
char *tok = NULL;
|
||||
|
||||
tmp_path[0] = '\0';
|
||||
|
||||
retro_assert(strlcpy(tmp_path, in_path,
|
||||
sizeof(tmp_path)) < sizeof(tmp_path));
|
||||
if ((tok = (char*)strrchr(path_basename(tmp_path), '.')))
|
||||
*tok = '\0';
|
||||
|
||||
fill_pathname_noext(out_path, tmp_path, replace, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname_noext:
|
||||
* @out_path : output path
|
||||
* @in_path : input path
|
||||
* @replace : what to replace
|
||||
* @size : buffer size of output path
|
||||
*
|
||||
* Appends a filename extension 'replace' to 'in_path', and outputs
|
||||
* result in 'out_path'.
|
||||
*
|
||||
* Assumes in_path has no extension. If an extension is still
|
||||
* present in 'in_path', it will be ignored.
|
||||
*
|
||||
*/
|
||||
void fill_pathname_noext(char *out_path, const char *in_path,
|
||||
const char *replace, size_t size)
|
||||
{
|
||||
retro_assert(strlcpy(out_path, in_path, size) < size);
|
||||
retro_assert(strlcat(out_path, replace, size) < size);
|
||||
}
|
||||
|
||||
char *find_last_slash(const char *str)
|
||||
{
|
||||
const char *slash = strrchr(str, '/');
|
||||
#ifdef _WIN32
|
||||
const char *backslash = strrchr(str, '\\');
|
||||
|
||||
if (backslash && ((slash && backslash > slash) || !slash))
|
||||
slash = backslash;
|
||||
#endif
|
||||
|
||||
return (char*)slash;
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname_slash:
|
||||
* @path : path
|
||||
* @size : size of path
|
||||
*
|
||||
* Assumes path is a directory. Appends a slash
|
||||
* if not already there.
|
||||
**/
|
||||
void fill_pathname_slash(char *path, size_t size)
|
||||
{
|
||||
size_t path_len = strlen(path);
|
||||
const char *last_slash = find_last_slash(path);
|
||||
|
||||
/* Try to preserve slash type. */
|
||||
if (last_slash && (last_slash != (path + path_len - 1)))
|
||||
{
|
||||
char join_str[2];
|
||||
|
||||
join_str[0] = '\0';
|
||||
|
||||
strlcpy(join_str, last_slash, sizeof(join_str));
|
||||
retro_assert(strlcat(path, join_str, size) < size);
|
||||
}
|
||||
else if (!last_slash)
|
||||
retro_assert(strlcat(path, path_default_slash(), size) < size);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname_dir:
|
||||
* @in_dir : input directory path
|
||||
* @in_basename : input basename to be appended to @in_dir
|
||||
* @replace : replacement to be appended to @in_basename
|
||||
* @size : size of buffer
|
||||
*
|
||||
* Appends basename of 'in_basename', to 'in_dir', along with 'replace'.
|
||||
* Basename of in_basename is the string after the last '/' or '\\',
|
||||
* i.e the filename without directories.
|
||||
*
|
||||
* If in_basename has no '/' or '\\', the whole 'in_basename' will be used.
|
||||
* 'size' is buffer size of 'in_dir'.
|
||||
*
|
||||
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
|
||||
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
|
||||
**/
|
||||
void fill_pathname_dir(char *in_dir, const char *in_basename,
|
||||
const char *replace, size_t size)
|
||||
{
|
||||
const char *base = NULL;
|
||||
|
||||
fill_pathname_slash(in_dir, size);
|
||||
base = path_basename(in_basename);
|
||||
retro_assert(strlcat(in_dir, base, size) < size);
|
||||
retro_assert(strlcat(in_dir, replace, size) < size);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname_base:
|
||||
* @out : output path
|
||||
* @in_path : input path
|
||||
* @size : size of output path
|
||||
*
|
||||
* Copies basename of @in_path into @out_path.
|
||||
**/
|
||||
void fill_pathname_base(char *out, const char *in_path, size_t size)
|
||||
{
|
||||
const char *ptr = path_basename(in_path);
|
||||
|
||||
if (!ptr)
|
||||
ptr = in_path;
|
||||
|
||||
retro_assert(strlcpy(out, ptr, size) < size);
|
||||
}
|
||||
|
||||
void fill_pathname_base_noext(char *out, const char *in_path, size_t size)
|
||||
{
|
||||
fill_pathname_base(out, in_path, size);
|
||||
path_remove_extension(out);
|
||||
}
|
||||
|
||||
void fill_pathname_base_ext(char *out, const char *in_path, const char *ext,
|
||||
size_t size)
|
||||
{
|
||||
fill_pathname_base_noext(out, in_path, size);
|
||||
strlcat(out, ext, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname_basedir:
|
||||
* @out_dir : output directory
|
||||
* @in_path : input path
|
||||
* @size : size of output directory
|
||||
*
|
||||
* Copies base directory of @in_path into @out_path.
|
||||
* If in_path is a path without any slashes (relative current directory),
|
||||
* @out_path will get path "./".
|
||||
**/
|
||||
void fill_pathname_basedir(char *out_dir,
|
||||
const char *in_path, size_t size)
|
||||
{
|
||||
if (out_dir != in_path)
|
||||
retro_assert(strlcpy(out_dir, in_path, size) < size);
|
||||
path_basedir(out_dir);
|
||||
}
|
||||
|
||||
void fill_pathname_basedir_noext(char *out_dir,
|
||||
const char *in_path, size_t size)
|
||||
{
|
||||
fill_pathname_basedir(out_dir, in_path, size);
|
||||
path_remove_extension(out_dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname_parent_dir:
|
||||
* @out_dir : output directory
|
||||
* @in_dir : input directory
|
||||
* @size : size of output directory
|
||||
*
|
||||
* Copies parent directory of @in_dir into @out_dir.
|
||||
* Assumes @in_dir is a directory. Keeps trailing '/'.
|
||||
**/
|
||||
void fill_pathname_parent_dir(char *out_dir,
|
||||
const char *in_dir, size_t size)
|
||||
{
|
||||
if (out_dir != in_dir)
|
||||
retro_assert(strlcpy(out_dir, in_dir, size) < size);
|
||||
path_parent_dir(out_dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_dated_filename:
|
||||
* @out_filename : output filename
|
||||
* @ext : extension of output filename
|
||||
* @size : buffer size of output filename
|
||||
*
|
||||
* Creates a 'dated' filename prefixed by 'RetroArch', and
|
||||
* concatenates extension (@ext) to it.
|
||||
*
|
||||
* E.g.:
|
||||
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
|
||||
**/
|
||||
void fill_dated_filename(char *out_filename,
|
||||
const char *ext, size_t size)
|
||||
{
|
||||
time_t cur_time = time(NULL);
|
||||
|
||||
strftime(out_filename, size,
|
||||
"RetroArch-%m%d-%H%M%S.", localtime(&cur_time));
|
||||
strlcat(out_filename, ext, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_str_dated_filename:
|
||||
* @out_filename : output filename
|
||||
* @in_str : input string
|
||||
* @ext : extension of output filename
|
||||
* @size : buffer size of output filename
|
||||
*
|
||||
* Creates a 'dated' filename prefixed by the string @in_str, and
|
||||
* concatenates extension (@ext) to it.
|
||||
*
|
||||
* E.g.:
|
||||
* out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
|
||||
**/
|
||||
void fill_str_dated_filename(char *out_filename,
|
||||
const char *in_str, const char *ext, size_t size)
|
||||
{
|
||||
char format[256];
|
||||
time_t cur_time = time(NULL);
|
||||
|
||||
format[0] = '\0';
|
||||
|
||||
strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", localtime(&cur_time));
|
||||
strlcpy(out_filename, in_str, size);
|
||||
strlcat(out_filename, format, size);
|
||||
strlcat(out_filename, ext, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* path_basedir:
|
||||
* @path : path
|
||||
*
|
||||
* Extracts base directory by mutating path.
|
||||
* Keeps trailing '/'.
|
||||
**/
|
||||
void path_basedir(char *path)
|
||||
{
|
||||
char *last = NULL;
|
||||
if (strlen(path) < 2)
|
||||
return;
|
||||
|
||||
last = find_last_slash(path);
|
||||
|
||||
if (last)
|
||||
last[1] = '\0';
|
||||
else
|
||||
snprintf(path, 3, ".%s", path_default_slash());
|
||||
}
|
||||
|
||||
/**
|
||||
* path_parent_dir:
|
||||
* @path : path
|
||||
*
|
||||
* Extracts parent directory by mutating path.
|
||||
* Assumes that path is a directory. Keeps trailing '/'.
|
||||
**/
|
||||
void path_parent_dir(char *path)
|
||||
{
|
||||
size_t len = strlen(path);
|
||||
if (len && path_char_is_slash(path[len - 1]))
|
||||
path[len - 1] = '\0';
|
||||
path_basedir(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* path_basename:
|
||||
* @path : path
|
||||
*
|
||||
* Get basename from @path.
|
||||
*
|
||||
* Returns: basename from path.
|
||||
**/
|
||||
const char *path_basename(const char *path)
|
||||
{
|
||||
/* We cut either at the first compression-related hash
|
||||
* or the last slash; whichever comes last */
|
||||
const char *last = find_last_slash(path);
|
||||
const char *delim = path_get_archive_delim(path);
|
||||
|
||||
if (delim)
|
||||
return delim + 1;
|
||||
|
||||
if (last)
|
||||
return last + 1;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_is_absolute:
|
||||
* @path : path
|
||||
*
|
||||
* Checks if @path is an absolute path or a relative path.
|
||||
*
|
||||
* Returns: true if path is absolute, false if path is relative.
|
||||
**/
|
||||
bool path_is_absolute(const char *path)
|
||||
{
|
||||
if (path[0] == '/')
|
||||
return true;
|
||||
#ifdef _WIN32
|
||||
/* Many roads lead to Rome ... */
|
||||
if (( strstr(path, "\\\\") == path)
|
||||
|| strstr(path, ":/")
|
||||
|| strstr(path, ":\\")
|
||||
|| strstr(path, ":\\\\"))
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_resolve_realpath:
|
||||
* @buf : buffer for path
|
||||
* @size : size of buffer
|
||||
*
|
||||
* Turns relative paths into absolute path.
|
||||
* If relative, rebases on current working dir.
|
||||
**/
|
||||
void path_resolve_realpath(char *buf, size_t size)
|
||||
{
|
||||
#ifndef RARCH_CONSOLE
|
||||
char tmp[PATH_MAX_LENGTH];
|
||||
|
||||
tmp[0] = '\0';
|
||||
|
||||
strlcpy(tmp, buf, sizeof(tmp));
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!_fullpath(buf, tmp, size))
|
||||
strlcpy(buf, tmp, size);
|
||||
#else
|
||||
retro_assert(size >= PATH_MAX_LENGTH);
|
||||
|
||||
/* NOTE: realpath() expects at least PATH_MAX_LENGTH bytes in buf.
|
||||
* Technically, PATH_MAX_LENGTH needn't be defined, but we rely on it anyways.
|
||||
* POSIX 2008 can automatically allocate for you,
|
||||
* but don't rely on that. */
|
||||
if (!realpath(tmp, buf))
|
||||
strlcpy(buf, tmp, size);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname_resolve_relative:
|
||||
* @out_path : output path
|
||||
* @in_refpath : input reference path
|
||||
* @in_path : input path
|
||||
* @size : size of @out_path
|
||||
*
|
||||
* Joins basedir of @in_refpath together with @in_path.
|
||||
* If @in_path is an absolute path, out_path = in_path.
|
||||
* E.g.: in_refpath = "/foo/bar/baz.a", in_path = "foobar.cg",
|
||||
* out_path = "/foo/bar/foobar.cg".
|
||||
**/
|
||||
void fill_pathname_resolve_relative(char *out_path,
|
||||
const char *in_refpath, const char *in_path, size_t size)
|
||||
{
|
||||
if (path_is_absolute(in_path))
|
||||
{
|
||||
retro_assert(strlcpy(out_path, in_path, size) < size);
|
||||
return;
|
||||
}
|
||||
|
||||
fill_pathname_basedir(out_path, in_refpath, size);
|
||||
retro_assert(strlcat(out_path, in_path, size) < size);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_pathname_join:
|
||||
* @out_path : output path
|
||||
* @dir : directory
|
||||
* @path : path
|
||||
* @size : size of output path
|
||||
*
|
||||
* Joins a directory (@dir) and path (@path) together.
|
||||
* Makes sure not to get two consecutive slashes
|
||||
* between directory and path.
|
||||
**/
|
||||
void fill_pathname_join(char *out_path,
|
||||
const char *dir, const char *path, size_t size)
|
||||
{
|
||||
if (out_path != dir)
|
||||
retro_assert(strlcpy(out_path, dir, size) < size);
|
||||
|
||||
if (*out_path)
|
||||
fill_pathname_slash(out_path, size);
|
||||
|
||||
retro_assert(strlcat(out_path, path, size) < size);
|
||||
}
|
||||
|
||||
static void fill_string_join(char *out_path,
|
||||
const char *append, size_t size)
|
||||
{
|
||||
if (*out_path)
|
||||
fill_pathname_slash(out_path, size);
|
||||
|
||||
retro_assert(strlcat(out_path, append, size) < size);
|
||||
}
|
||||
|
||||
void fill_pathname_join_special_ext(char *out_path,
|
||||
const char *dir, const char *path,
|
||||
const char *last, const char *ext,
|
||||
size_t size)
|
||||
{
|
||||
fill_pathname_join(out_path, dir, path, size);
|
||||
fill_string_join(out_path, last, size);
|
||||
strlcat(out_path, ext, size);
|
||||
}
|
||||
|
||||
void fill_pathname_join_concat(char *out_path,
|
||||
const char *dir, const char *path,
|
||||
const char *concat,
|
||||
size_t size)
|
||||
{
|
||||
fill_pathname_join(out_path, dir, path, size);
|
||||
strlcat(out_path, concat, size);
|
||||
}
|
||||
|
||||
void fill_pathname_join_noext(char *out_path,
|
||||
const char *dir, const char *path, size_t size)
|
||||
{
|
||||
fill_pathname_join(out_path, dir, path, size);
|
||||
path_remove_extension(out_path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fill_pathname_join_delim:
|
||||
* @out_path : output path
|
||||
* @dir : directory
|
||||
* @path : path
|
||||
* @delim : delimiter
|
||||
* @size : size of output path
|
||||
*
|
||||
* Joins a directory (@dir) and path (@path) together
|
||||
* using the given delimiter (@delim).
|
||||
**/
|
||||
void fill_pathname_join_delim(char *out_path, const char *dir,
|
||||
const char *path, const char delim, size_t size)
|
||||
{
|
||||
size_t copied = strlcpy(out_path, dir, size);
|
||||
retro_assert(copied < size+1);
|
||||
|
||||
out_path[copied] = delim;
|
||||
out_path[copied+1] = '\0';
|
||||
|
||||
retro_assert(strlcat(out_path, path, size) < size);
|
||||
}
|
||||
|
||||
void fill_pathname_join_delim_concat(char *out_path, const char *dir,
|
||||
const char *path, const char delim, const char *concat,
|
||||
size_t size)
|
||||
{
|
||||
fill_pathname_join_delim(out_path, dir, path, delim, size);
|
||||
strlcat(out_path, concat, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_short_pathname_representation:
|
||||
* @out_rep : output representation
|
||||
* @in_path : input path
|
||||
* @size : size of output representation
|
||||
*
|
||||
* Generates a short representation of path. It should only
|
||||
* be used for displaying the result; the output representation is not
|
||||
* binding in any meaningful way (for a normal path, this is the same as basename)
|
||||
* In case of more complex URLs, this should cut everything except for
|
||||
* the main image file.
|
||||
*
|
||||
* E.g.: "/path/to/game.img" -> game.img
|
||||
* "/path/to/myarchive.7z#folder/to/game.img" -> game.img
|
||||
*/
|
||||
void fill_short_pathname_representation(char* out_rep,
|
||||
const char *in_path, size_t size)
|
||||
{
|
||||
char path_short[PATH_MAX_LENGTH];
|
||||
|
||||
path_short[0] = '\0';
|
||||
|
||||
fill_pathname(path_short, path_basename(in_path), "",
|
||||
sizeof(path_short));
|
||||
|
||||
strlcpy(out_rep, path_short, size);
|
||||
}
|
||||
|
||||
void fill_short_pathname_representation_noext(char* out_rep,
|
||||
const char *in_path, size_t size)
|
||||
{
|
||||
fill_short_pathname_representation(out_rep, in_path, size);
|
||||
path_remove_extension(out_rep);
|
||||
}
|
205
libretro-common/file/nbio/nbio_stdio.c
Normal file
205
libretro-common/file/nbio/nbio_stdio.c
Normal file
@ -0,0 +1,205 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <file/nbio.h>
|
||||
|
||||
struct nbio_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;
|
||||
};
|
||||
|
||||
static const char * modes[]={ "rb", "wb", "r+b", "rb", "wb", "r+b" };
|
||||
|
||||
struct nbio_t* nbio_open(const char * filename, unsigned mode)
|
||||
{
|
||||
void *buf = NULL;
|
||||
struct nbio_t* handle = NULL;
|
||||
size_t len = 0;
|
||||
FILE* f = fopen(filename, modes[mode]);
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
handle = (struct nbio_t*)malloc(sizeof(struct nbio_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 (!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;
|
||||
}
|
||||
|
||||
void nbio_begin_read(struct nbio_t* handle)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void nbio_begin_write(struct nbio_t* handle)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool nbio_iterate(struct nbio_t* handle)
|
||||
{
|
||||
size_t amount = 65536;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void nbio_resize(struct nbio_t* handle, size_t len)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void* nbio_get_ptr(struct nbio_t* handle, size_t* len)
|
||||
{
|
||||
if (!handle)
|
||||
return NULL;
|
||||
if (len)
|
||||
*len = handle->len;
|
||||
if (handle->op == -1)
|
||||
return handle->data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void nbio_cancel(struct nbio_t* handle)
|
||||
{
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
handle->op = -1;
|
||||
handle->progress = handle->len;
|
||||
}
|
||||
|
||||
void nbio_free(struct nbio_t* handle)
|
||||
{
|
||||
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);
|
||||
}
|
163
libretro-common/file/retro_dirent.c
Normal file
163
libretro-common/file/retro_dirent.c
Normal file
@ -0,0 +1,163 @@
|
||||
/* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <retro_common.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <file/file_path.h>
|
||||
#include <retro_dirent.h>
|
||||
#include <encodings/utf.h>
|
||||
|
||||
struct RDIR *retro_opendir(const char *name)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
char path_buf[1024];
|
||||
#endif
|
||||
struct RDIR *rdir = (struct RDIR*)calloc(1, sizeof(*rdir));
|
||||
|
||||
if (!rdir)
|
||||
return NULL;
|
||||
|
||||
#if defined(_WIN32)
|
||||
path_buf[0] = '\0';
|
||||
snprintf(path_buf, sizeof(path_buf), "%s\\*", name);
|
||||
rdir->directory = FindFirstFile(path_buf, &rdir->entry);
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
rdir->directory = sceIoDopen(name);
|
||||
#elif defined(_3DS)
|
||||
rdir->directory = (name && *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
|
||||
|
||||
return rdir;
|
||||
}
|
||||
|
||||
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)
|
||||
return (FindNextFile(rdir->directory, &rdir->entry) != 0);
|
||||
|
||||
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)
|
||||
return 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);
|
||||
#elif 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 path_is_directory(path);
|
||||
return false;
|
||||
#else
|
||||
/* dirent struct doesn't have d_type, do it the slow way ... */
|
||||
return path_is_directory(path);
|
||||
#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);
|
||||
}
|
626
libretro-common/formats/bmp/rbmp.c
Normal file
626
libretro-common/formats/bmp/rbmp.c
Normal file
@ -0,0 +1,626 @@
|
||||
/* 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_assert.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:
|
||||
retro_assert(0);
|
||||
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
|
||||
{
|
||||
retro_assert(hsz == 108 || hsz == 124);
|
||||
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;
|
||||
}
|
230
libretro-common/formats/bmp/rbmp_encode.c
Normal file
230
libretro-common/formats/bmp/rbmp_encode.c
Normal file
@ -0,0 +1,230 @@
|
||||
/* 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, RFILE_MODE_WRITE, -1);
|
||||
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;
|
||||
}
|
315
libretro-common/formats/image_texture.c
Normal file
315
libretro-common/formats/image_texture.c
Normal file
@ -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;
|
||||
}
|
282
libretro-common/formats/image_transfer.c
Normal file
282
libretro-common/formats/image_transfer.c
Normal file
@ -0,0 +1,282 @@
|
||||
/* 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_free((rpng_t*)data);
|
||||
#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;
|
||||
}
|
2626
libretro-common/formats/jpeg/rjpeg.c
Normal file
2626
libretro-common/formats/jpeg/rjpeg.c
Normal file
File diff suppressed because it is too large
Load Diff
316
libretro-common/formats/json/jsonsax.c
Normal file
316
libretro-common/formats/json/jsonsax.c
Normal file
@ -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( *state->json ) )
|
||||
state->json++;
|
||||
}
|
||||
|
||||
static INLINE void skip_digits( state_t* state )
|
||||
{
|
||||
while ( isdigit( *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( *state->json ) )
|
||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||
|
||||
skip_digits( state );
|
||||
|
||||
if ( *state->json == '.' )
|
||||
{
|
||||
state->json++;
|
||||
|
||||
if ( !isdigit( *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( *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;
|
||||
}
|
3931
libretro-common/formats/json/jsonsax_full.c
Normal file
3931
libretro-common/formats/json/jsonsax_full.c
Normal file
File diff suppressed because it is too large
Load Diff
1195
libretro-common/formats/png/rpng.c
Normal file
1195
libretro-common/formats/png/rpng.c
Normal file
File diff suppressed because it is too large
Load Diff
388
libretro-common/formats/png/rpng_encode.c
Normal file
388
libretro-common/formats/png/rpng_encode.c
Normal file
@ -0,0 +1,388 @@
|
||||
/* 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, RFILE_MODE_WRITE, -1);
|
||||
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:
|
||||
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);
|
||||
}
|
56
libretro-common/formats/png/rpng_internal.h
Normal file
56
libretro-common/formats/png/rpng_internal.h
Normal file
@ -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
|
447
libretro-common/formats/tga/rtga.c
Normal file
447
libretro-common/formats/tga/rtga.c
Normal file
@ -0,0 +1,447 @@
|
||||
/* 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:
|
||||
retro_assert(0);
|
||||
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;
|
||||
}
|
186
libretro-common/formats/wav/rwav.c
Normal file
186
libretro-common/formats/wav/rwav.c
Normal file
@ -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);
|
||||
}
|
493
libretro-common/formats/xml/rxml.c
Normal file
493
libretro-common/formats/xml/rxml.c
Normal file
@ -0,0 +1,493 @@
|
||||
/* 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)
|
||||
{
|
||||
#ifndef RXML_TEST
|
||||
RARCH_WARN("Using RXML as drop in for libxml2. Behavior might be very buggy.\n");
|
||||
#endif
|
||||
|
||||
char *memory_buffer = NULL;
|
||||
char *new_memory_buffer = NULL;
|
||||
const char *mem_ptr = NULL;
|
||||
long len = 0;
|
||||
RFILE *file = filestream_open(path, RFILE_MODE_READ, -1);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
rxml_document_t *doc = (rxml_document_t*)calloc(1, sizeof(*doc));
|
||||
if (!doc)
|
||||
goto error;
|
||||
|
||||
filestream_seek(file, 0, SEEK_END);
|
||||
len = filestream_tell(file);
|
||||
filestream_rewind(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;
|
||||
}
|
||||
|
27
libretro-common/formats/xml/test/Makefile
Normal file
27
libretro-common/formats/xml/test/Makefile
Normal file
@ -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
|
||||
|
67
libretro-common/formats/xml/test/rxml_test.c
Normal file
67
libretro-common/formats/xml/test/rxml_test.c
Normal file
@ -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]);
|
||||
}
|
||||
|
330
libretro-common/gfx/gl_capabilities.c
Normal file
330
libretro-common/gfx/gl_capabilities.c
Normal file
@ -0,0 +1,330 @@
|
||||
/* 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;
|
||||
#elif defined(HAVE_FBO)
|
||||
if (!gl_query_core_context_in_use() && !gl_query_extension("ARB_framebuffer_object")
|
||||
&& !gl_query_extension("EXT_framebuffer_object"))
|
||||
return false;
|
||||
|
||||
if (glGenFramebuffers
|
||||
&& glBindFramebuffer
|
||||
&& glFramebufferTexture2D
|
||||
&& glCheckFramebufferStatus
|
||||
&& glDeleteFramebuffers
|
||||
&& glGenRenderbuffers
|
||||
&& glBindRenderbuffer
|
||||
&& glFramebufferRenderbuffer
|
||||
&& glRenderbufferStorage
|
||||
&& glDeleteRenderbuffers)
|
||||
return true;
|
||||
break;
|
||||
#else
|
||||
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;
|
||||
#elif defined(HAVE_FBO)
|
||||
if (gl_query_core_context_in_use() ||
|
||||
(gl_query_extension("EXT_texture_sRGB")
|
||||
&& gl_query_extension("ARB_framebuffer_sRGB")))
|
||||
return true;
|
||||
#endif
|
||||
break;
|
||||
case GL_CAPS_FP_FBO:
|
||||
/* GLES - No extensions for float FBO currently. */
|
||||
#ifndef HAVE_OPENGLES
|
||||
#ifdef HAVE_FBO
|
||||
/* Float FBO is core in 3.2. */
|
||||
if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float"))
|
||||
return true;
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
case GL_CAPS_BGRA8888:
|
||||
#ifdef HAVE_OPENGLES
|
||||
/* There are both APPLE and EXT variants. */
|
||||
if (gl_query_extension("BGRA8888"))
|
||||
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:
|
||||
if (gl_query_extension("EXT_texture_storage"))
|
||||
return true;
|
||||
break;
|
||||
case GL_CAPS_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
817
libretro-common/gfx/scaler/pixconv.c
Normal file
817
libretro-common/gfx/scaler/pixconv.c
Normal file
@ -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);
|
||||
}
|
||||
|
343
libretro-common/gfx/scaler/scaler.c
Normal file
343
libretro-common/gfx/scaler/scaler.c
Normal file
@ -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);
|
||||
}
|
244
libretro-common/gfx/scaler/scaler_filter.c
Normal file
244
libretro-common/gfx/scaler/scaler_filter.c
Normal file
@ -0,0 +1,244 @@
|
||||
/* 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 <math.h>
|
||||
|
||||
#include <gfx/scaler/filter.h>
|
||||
#include <gfx/scaler/scaler_int.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_inline.h>
|
||||
#include <filters.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);
|
||||
}
|
||||
|
261
libretro-common/gfx/scaler/scaler_int.c
Normal file
261
libretro-common/gfx/scaler/scaler_int.c
Normal file
@ -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];
|
||||
}
|
||||
}
|
||||
|
2278
libretro-common/glsm/glsm.c
Normal file
2278
libretro-common/glsm/glsm.c
Normal file
File diff suppressed because it is too large
Load Diff
12
libretro-common/glsym/README.md
Normal file
12
libretro-common/glsym/README.md
Normal file
@ -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
|
||||
|
132
libretro-common/glsym/glgen.py
Normal file
132
libretro-common/glsym/glgen.py
Normal file
@ -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)
|
||||
|
410
libretro-common/glsym/glsym_es2.c
Normal file
410
libretro-common/glsym/glsym_es2.c
Normal file
@ -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;
|
||||
|
414
libretro-common/glsym/glsym_es3.c
Normal file
414
libretro-common/glsym/glsym_es3.c
Normal file
@ -0,0 +1,414 @@
|
||||
#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(GetQueryObjecti64vEXT),
|
||||
SYM(GetQueryObjectui64vEXT),
|
||||
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;
|
||||
RGLSYMGLGETQUERYOBJECTI64VEXTPROC __rglgen_glGetQueryObjecti64vEXT;
|
||||
RGLSYMGLGETQUERYOBJECTUI64VEXTPROC __rglgen_glGetQueryObjectui64vEXT;
|
||||
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;
|
||||
|
2083
libretro-common/glsym/glsym_gl.c
Normal file
2083
libretro-common/glsym/glsym_gl.c
Normal file
File diff suppressed because it is too large
Load Diff
43
libretro-common/glsym/rglgen.c
Normal file
43
libretro-common/glsym/rglgen.c
Normal file
@ -0,0 +1,43 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this libretro SDK code part (glsym).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* 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 <glsym/rglgen.h>
|
||||
#include <glsym/glsym.h>
|
||||
|
||||
void rglgen_resolve_symbols_custom(rglgen_proc_address_t proc,
|
||||
const struct rglgen_sym_map *map)
|
||||
{
|
||||
for (; map->sym; map++)
|
||||
{
|
||||
rglgen_func_t func = proc(map->sym);
|
||||
memcpy(map->ptr, &func, sizeof(func));
|
||||
}
|
||||
}
|
||||
|
||||
void rglgen_resolve_symbols(rglgen_proc_address_t proc)
|
||||
{
|
||||
rglgen_resolve_symbols_custom(proc, rglgen_symbol_map);
|
||||
}
|
||||
|
132
libretro-common/glsym/rglgen.py
Normal file
132
libretro-common/glsym/rglgen.py
Normal file
@ -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('#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)
|
||||
|
||||
|
559
libretro-common/hash/rhash.c
Normal file
559
libretro-common/hash/rhash.c
Normal file
@ -0,0 +1,559 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rhash.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>
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <rhash.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_endianness.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define LSL32(x, n) ((uint32_t)(x) << (n))
|
||||
#define LSR32(x, n) ((uint32_t)(x) >> (n))
|
||||
#define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n)))
|
||||
|
||||
/* First 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 */
|
||||
static const uint32_t T_H[8] = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
};
|
||||
|
||||
/* First 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 */
|
||||
static const uint32_t T_K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
};
|
||||
|
||||
/* SHA256 implementation from bSNES. Written by valditx. */
|
||||
|
||||
struct sha256_ctx
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8_t u8[64];
|
||||
uint32_t u32[16];
|
||||
} in;
|
||||
unsigned inlen;
|
||||
|
||||
uint32_t w[64];
|
||||
uint32_t h[8];
|
||||
uint64_t len;
|
||||
};
|
||||
|
||||
static void sha256_init(struct sha256_ctx *p)
|
||||
{
|
||||
memset(p, 0, sizeof(struct sha256_ctx));
|
||||
memcpy(p->h, T_H, sizeof(T_H));
|
||||
}
|
||||
|
||||
static void sha256_block(struct sha256_ctx *p)
|
||||
{
|
||||
unsigned i;
|
||||
uint32_t s0, s1;
|
||||
uint32_t a, b, c, d, e, f, g, h;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
p->w[i] = load32be(p->in.u32 + i);
|
||||
|
||||
for (i = 16; i < 64; i++)
|
||||
{
|
||||
s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3);
|
||||
s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10);
|
||||
p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1;
|
||||
}
|
||||
|
||||
a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3];
|
||||
e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7];
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
uint32_t t1, t2, maj, ch;
|
||||
|
||||
s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22);
|
||||
maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
t2 = s0 + maj;
|
||||
s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25);
|
||||
ch = (e & f) ^ (~e & g);
|
||||
t1 = h + s1 + ch + T_K[i] + p->w[i];
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d;
|
||||
p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h;
|
||||
|
||||
/* Next block */
|
||||
p->inlen = 0;
|
||||
}
|
||||
|
||||
static void sha256_chunk(struct sha256_ctx *p,
|
||||
const uint8_t *s, unsigned len)
|
||||
{
|
||||
p->len += len;
|
||||
|
||||
while (len)
|
||||
{
|
||||
unsigned l = 64 - p->inlen;
|
||||
|
||||
if (len < l)
|
||||
l = len;
|
||||
|
||||
memcpy(p->in.u8 + p->inlen, s, l);
|
||||
|
||||
s += l;
|
||||
p->inlen += l;
|
||||
len -= l;
|
||||
|
||||
if (p->inlen == 64)
|
||||
sha256_block(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void sha256_final(struct sha256_ctx *p)
|
||||
{
|
||||
uint64_t len;
|
||||
p->in.u8[p->inlen++] = 0x80;
|
||||
|
||||
if (p->inlen > 56)
|
||||
{
|
||||
memset(p->in.u8 + p->inlen, 0, 64 - p->inlen);
|
||||
sha256_block(p);
|
||||
}
|
||||
|
||||
memset(p->in.u8 + p->inlen, 0, 56 - p->inlen);
|
||||
|
||||
len = p->len << 3;
|
||||
store32be(p->in.u32 + 14, (uint32_t)(len >> 32));
|
||||
store32be(p->in.u32 + 15, (uint32_t)len);
|
||||
sha256_block(p);
|
||||
}
|
||||
|
||||
static void sha256_subhash(struct sha256_ctx *p, uint32_t *t)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < 8; i++)
|
||||
store32be(t++, p->h[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* sha256_hash:
|
||||
* @s : Output.
|
||||
* @in : Input.
|
||||
* @size : Size of @s.
|
||||
*
|
||||
* Hashes SHA256 and outputs a human readable string.
|
||||
**/
|
||||
void sha256_hash(char *s, const uint8_t *in, size_t size)
|
||||
{
|
||||
unsigned i;
|
||||
struct sha256_ctx sha;
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t u32[8];
|
||||
uint8_t u8[32];
|
||||
} shahash;
|
||||
|
||||
sha256_init(&sha);
|
||||
sha256_chunk(&sha, in, (unsigned)size);
|
||||
sha256_final(&sha);
|
||||
sha256_subhash(&sha, shahash.u32);
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
snprintf(s + 2 * i, 3, "%02x", (unsigned)shahash.u8[i]);
|
||||
}
|
||||
|
||||
#ifndef HAVE_ZLIB
|
||||
/* Zlib CRC32. */
|
||||
static const uint32_t crc32_table[256] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
uint32_t crc32_adjust(uint32_t checksum, uint8_t input)
|
||||
{
|
||||
return ((checksum >> 8) & 0x00ffffff) ^ crc32_table[(checksum ^ input) & 0xff];
|
||||
}
|
||||
|
||||
uint32_t crc32_calculate(const uint8_t *data, size_t length)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t checksum = ~0;
|
||||
for (i = 0; i < length; i++)
|
||||
checksum = crc32_adjust(checksum, data[i]);
|
||||
return ~checksum;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* SHA-1 implementation. */
|
||||
|
||||
/*
|
||||
* sha1.c
|
||||
*
|
||||
* Copyright (C) 1998, 2009
|
||||
* Paul E. Jones <paulej@packetizer.com>
|
||||
* All Rights Reserved
|
||||
*
|
||||
*****************************************************************************
|
||||
* $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $
|
||||
*****************************************************************************
|
||||
*
|
||||
* Description:
|
||||
* This file implements the Secure Hashing Standard as defined
|
||||
* in FIPS PUB 180-1 published April 17, 1995.
|
||||
*
|
||||
* The Secure Hashing Standard, which uses the Secure Hashing
|
||||
* Algorithm (SHA), produces a 160-bit message digest for a
|
||||
* given data stream. In theory, it is highly improbable that
|
||||
* two messages will produce the same message digest. Therefore,
|
||||
* this algorithm can serve as a means of providing a "fingerprint"
|
||||
* for a message.
|
||||
*
|
||||
* Portability Issues:
|
||||
* SHA-1 is defined in terms of 32-bit "words". This code was
|
||||
* written with the expectation that the processor has at least
|
||||
* a 32-bit machine word size. If the machine word size is larger,
|
||||
* the code should still function properly. One caveat to that
|
||||
* is that the input functions taking characters and character
|
||||
* arrays assume that only 8 bits of information are stored in each
|
||||
* character.
|
||||
*
|
||||
* Caveats:
|
||||
* SHA-1 is designed to work with messages less than 2^64 bits
|
||||
* long. Although SHA-1 allows a message digest to be generated for
|
||||
* messages of any number of bits less than 2^64, this
|
||||
* implementation only works with messages with a length that is a
|
||||
* multiple of the size of an 8-bit character.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Define the circular shift macro */
|
||||
#define SHA1CircularShift(bits,word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32-(bits))))
|
||||
|
||||
static void SHA1Reset(SHA1Context *context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
context->Length_Low = 0;
|
||||
context->Length_High = 0;
|
||||
context->Message_Block_Index = 0;
|
||||
|
||||
context->Message_Digest[0] = 0x67452301;
|
||||
context->Message_Digest[1] = 0xEFCDAB89;
|
||||
context->Message_Digest[2] = 0x98BADCFE;
|
||||
context->Message_Digest[3] = 0x10325476;
|
||||
context->Message_Digest[4] = 0xC3D2E1F0;
|
||||
|
||||
context->Computed = 0;
|
||||
context->Corrupted = 0;
|
||||
}
|
||||
|
||||
static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
{
|
||||
const unsigned K[] = /* Constants defined in SHA-1 */
|
||||
{
|
||||
0x5A827999,
|
||||
0x6ED9EBA1,
|
||||
0x8F1BBCDC,
|
||||
0xCA62C1D6
|
||||
};
|
||||
int t; /* Loop counter */
|
||||
unsigned temp; /* Temporary word value */
|
||||
unsigned W[80]; /* Word sequence */
|
||||
unsigned A, B, C, D, E; /* Word buffers */
|
||||
|
||||
/* Initialize the first 16 words in the array W */
|
||||
for(t = 0; t < 16; t++)
|
||||
{
|
||||
W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
|
||||
W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
|
||||
W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
|
||||
W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
|
||||
}
|
||||
|
||||
for(t = 16; t < 80; t++)
|
||||
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
||||
|
||||
A = context->Message_Digest[0];
|
||||
B = context->Message_Digest[1];
|
||||
C = context->Message_Digest[2];
|
||||
D = context->Message_Digest[3];
|
||||
E = context->Message_Digest[4];
|
||||
|
||||
for(t = 0; t < 20; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | ((~B) & D)) + E + W[t] + K[0];
|
||||
temp &= 0xFFFFFFFF;
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 20; t < 40; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
|
||||
temp &= 0xFFFFFFFF;
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 40; t < 60; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
|
||||
temp &= 0xFFFFFFFF;
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 60; t < 80; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
|
||||
temp &= 0xFFFFFFFF;
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
context->Message_Digest[0] =
|
||||
(context->Message_Digest[0] + A) & 0xFFFFFFFF;
|
||||
context->Message_Digest[1] =
|
||||
(context->Message_Digest[1] + B) & 0xFFFFFFFF;
|
||||
context->Message_Digest[2] =
|
||||
(context->Message_Digest[2] + C) & 0xFFFFFFFF;
|
||||
context->Message_Digest[3] =
|
||||
(context->Message_Digest[3] + D) & 0xFFFFFFFF;
|
||||
context->Message_Digest[4] =
|
||||
(context->Message_Digest[4] + E) & 0xFFFFFFFF;
|
||||
|
||||
context->Message_Block_Index = 0;
|
||||
}
|
||||
|
||||
static void SHA1PadMessage(SHA1Context *context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check to see if the current message block is too small to hold
|
||||
* the initial padding bits and length. If so, we will pad the
|
||||
* block, process it, and then continue padding into a second
|
||||
* block.
|
||||
*/
|
||||
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||
|
||||
if (context->Message_Block_Index > 55)
|
||||
{
|
||||
while(context->Message_Block_Index < 64)
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
|
||||
while(context->Message_Block_Index < 56)
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
|
||||
/* Store the message length as the last 8 octets */
|
||||
context->Message_Block[56] = (context->Length_High >> 24) & 0xFF;
|
||||
context->Message_Block[57] = (context->Length_High >> 16) & 0xFF;
|
||||
context->Message_Block[58] = (context->Length_High >> 8) & 0xFF;
|
||||
context->Message_Block[59] = (context->Length_High) & 0xFF;
|
||||
context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF;
|
||||
context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF;
|
||||
context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF;
|
||||
context->Message_Block[63] = (context->Length_Low) & 0xFF;
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
|
||||
static int SHA1Result(SHA1Context *context)
|
||||
{
|
||||
if (context->Corrupted)
|
||||
return 0;
|
||||
|
||||
if (!context->Computed)
|
||||
{
|
||||
SHA1PadMessage(context);
|
||||
context->Computed = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void SHA1Input(SHA1Context *context,
|
||||
const unsigned char *message_array,
|
||||
unsigned length)
|
||||
{
|
||||
if (!length)
|
||||
return;
|
||||
|
||||
if (context->Computed || context->Corrupted)
|
||||
{
|
||||
context->Corrupted = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
while(length-- && !context->Corrupted)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] =
|
||||
(*message_array & 0xFF);
|
||||
|
||||
context->Length_Low += 8;
|
||||
/* Force it to 32 bits */
|
||||
context->Length_Low &= 0xFFFFFFFF;
|
||||
if (context->Length_Low == 0)
|
||||
{
|
||||
context->Length_High++;
|
||||
/* Force it to 32 bits */
|
||||
context->Length_High &= 0xFFFFFFFF;
|
||||
if (context->Length_High == 0)
|
||||
context->Corrupted = 1; /* Message is too long */
|
||||
}
|
||||
|
||||
if (context->Message_Block_Index == 64)
|
||||
SHA1ProcessMessageBlock(context);
|
||||
|
||||
message_array++;
|
||||
}
|
||||
}
|
||||
|
||||
int sha1_calculate(const char *path, char *result)
|
||||
{
|
||||
SHA1Context sha;
|
||||
unsigned char buff[4096];
|
||||
int rv = 1;
|
||||
RFILE *fd = filestream_open(path, RFILE_MODE_READ, -1);
|
||||
|
||||
if (!fd)
|
||||
goto error;
|
||||
|
||||
buff[0] = '\0';
|
||||
|
||||
SHA1Reset(&sha);
|
||||
|
||||
do
|
||||
{
|
||||
rv = (int)filestream_read(fd, buff, 4096);
|
||||
if (rv < 0)
|
||||
goto error;
|
||||
|
||||
SHA1Input(&sha, buff, rv);
|
||||
}while(rv);
|
||||
|
||||
if (!SHA1Result(&sha))
|
||||
goto error;
|
||||
|
||||
sprintf(result, "%08X%08X%08X%08X%08X",
|
||||
sha.Message_Digest[0],
|
||||
sha.Message_Digest[1],
|
||||
sha.Message_Digest[2],
|
||||
sha.Message_Digest[3], sha.Message_Digest[4]);
|
||||
|
||||
filestream_close(fd);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (fd)
|
||||
filestream_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t djb2_calculate(const char *str)
|
||||
{
|
||||
const unsigned char *aux = (const unsigned char*)str;
|
||||
uint32_t hash = 5381;
|
||||
|
||||
while ( *aux )
|
||||
hash = ( hash << 5 ) + hash + *aux++;
|
||||
|
||||
return hash;
|
||||
}
|
93
libretro-common/include/audio/audio_mix.h
Normal file
93
libretro-common/include/audio/audio_mix.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_mix.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 __LIBRETRO_SDK_AUDIO_MIX_H__
|
||||
#define __LIBRETRO_SDK_AUDIO_MIX_H__
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <formats/rwav.h>
|
||||
#include <audio/audio_resampler.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *buf;
|
||||
int16_t *upsample_buf;
|
||||
float *float_buf;
|
||||
float *float_resample_buf;
|
||||
int16_t *resample_buf;
|
||||
ssize_t len;
|
||||
size_t resample_len;
|
||||
rwav_t *rwav;
|
||||
int sample_rate;
|
||||
bool resample;
|
||||
const retro_resampler_t *resampler;
|
||||
void *resampler_data;
|
||||
double ratio;
|
||||
} audio_chunk_t;
|
||||
|
||||
#if defined(__SSE2__)
|
||||
#define audio_mix_volume audio_mix_volume_SSE2
|
||||
|
||||
void audio_mix_volume_SSE2(float *out,
|
||||
const float *in, float vol, size_t samples);
|
||||
#else
|
||||
#define audio_mix_volume audio_mix_volume_C
|
||||
#endif
|
||||
|
||||
void audio_mix_volume_C(float *dst, const float *src, float vol, size_t samples);
|
||||
|
||||
void audio_mix_free_chunk(audio_chunk_t *chunk);
|
||||
|
||||
audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate);
|
||||
|
||||
size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk);
|
||||
|
||||
/**
|
||||
* 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, (if necessary) resampled into the desired output rate.
|
||||
**/
|
||||
int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk, unsigned channel, size_t sample);
|
||||
|
||||
int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk);
|
||||
|
||||
int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
74
libretro-common/include/audio/audio_mixer.h
Normal file
74
libretro-common/include/audio/audio_mixer.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_mixer.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 __LIBRETRO_SDK_AUDIO_MIXER__H
|
||||
#define __LIBRETRO_SDK_AUDIO_MIXER__H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <boolean.h>
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
enum audio_mixer_type
|
||||
{
|
||||
AUDIO_MIXER_TYPE_NONE = 0,
|
||||
AUDIO_MIXER_TYPE_WAV,
|
||||
AUDIO_MIXER_TYPE_OGG
|
||||
};
|
||||
|
||||
typedef struct audio_mixer_sound audio_mixer_sound_t;
|
||||
typedef struct audio_mixer_voice audio_mixer_voice_t;
|
||||
|
||||
typedef void (*audio_mixer_stop_cb_t)(audio_mixer_sound_t* sound, unsigned reason);
|
||||
|
||||
/* Reasons passed to the stop callback. */
|
||||
#define AUDIO_MIXER_SOUND_FINISHED 0
|
||||
#define AUDIO_MIXER_SOUND_STOPPED 1
|
||||
#define AUDIO_MIXER_SOUND_REPEATED 2
|
||||
|
||||
void audio_mixer_init(unsigned rate);
|
||||
|
||||
void audio_mixer_done(void);
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size);
|
||||
|
||||
void audio_mixer_destroy(audio_mixer_sound_t* sound);
|
||||
|
||||
audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound,
|
||||
bool repeat, float volume, audio_mixer_stop_cb_t stop_cb);
|
||||
|
||||
void audio_mixer_stop(audio_mixer_voice_t* voice);
|
||||
|
||||
void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bool override);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
184
libretro-common/include/audio/audio_resampler.h
Normal file
184
libretro-common/include/audio/audio_resampler.h
Normal file
@ -0,0 +1,184 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_resampler.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 __LIBRETRO_SDK_AUDIO_RESAMPLER_DRIVER_H
|
||||
#define __LIBRETRO_SDK_AUDIO_RESAMPLER_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#define RESAMPLER_SIMD_SSE (1 << 0)
|
||||
#define RESAMPLER_SIMD_SSE2 (1 << 1)
|
||||
#define RESAMPLER_SIMD_VMX (1 << 2)
|
||||
#define RESAMPLER_SIMD_VMX128 (1 << 3)
|
||||
#define RESAMPLER_SIMD_AVX (1 << 4)
|
||||
#define RESAMPLER_SIMD_NEON (1 << 5)
|
||||
#define RESAMPLER_SIMD_SSE3 (1 << 6)
|
||||
#define RESAMPLER_SIMD_SSSE3 (1 << 7)
|
||||
#define RESAMPLER_SIMD_MMX (1 << 8)
|
||||
#define RESAMPLER_SIMD_MMXEXT (1 << 9)
|
||||
#define RESAMPLER_SIMD_SSE4 (1 << 10)
|
||||
#define RESAMPLER_SIMD_SSE42 (1 << 11)
|
||||
#define RESAMPLER_SIMD_AVX2 (1 << 12)
|
||||
#define RESAMPLER_SIMD_VFPU (1 << 13)
|
||||
#define RESAMPLER_SIMD_PS (1 << 14)
|
||||
|
||||
/* A bit-mask of all supported SIMD instruction sets.
|
||||
* Allows an implementation to pick different
|
||||
* resampler_implementation structs.
|
||||
*/
|
||||
typedef unsigned resampler_simd_mask_t;
|
||||
|
||||
#define RESAMPLER_API_VERSION 1
|
||||
|
||||
struct resampler_data
|
||||
{
|
||||
const float *data_in;
|
||||
float *data_out;
|
||||
|
||||
size_t input_frames;
|
||||
size_t output_frames;
|
||||
|
||||
double ratio;
|
||||
};
|
||||
|
||||
/* Returns true if config key was found. Otherwise,
|
||||
* returns false, and sets value to default value.
|
||||
*/
|
||||
typedef int (*resampler_config_get_float_t)(void *userdata,
|
||||
const char *key, float *value, float default_value);
|
||||
|
||||
typedef int (*resampler_config_get_int_t)(void *userdata,
|
||||
const char *key, int *value, int default_value);
|
||||
|
||||
/* Allocates an array with values. free() with resampler_config_free_t. */
|
||||
typedef int (*resampler_config_get_float_array_t)(void *userdata,
|
||||
const char *key, float **values, unsigned *out_num_values,
|
||||
const float *default_values, unsigned num_default_values);
|
||||
|
||||
typedef int (*resampler_config_get_int_array_t)(void *userdata,
|
||||
const char *key, int **values, unsigned *out_num_values,
|
||||
const int *default_values, unsigned num_default_values);
|
||||
|
||||
typedef int (*resampler_config_get_string_t)(void *userdata,
|
||||
const char *key, char **output, const char *default_output);
|
||||
|
||||
/* Calls free() in host runtime. Sometimes needed on Windows.
|
||||
* free() on NULL is fine. */
|
||||
typedef void (*resampler_config_free_t)(void *ptr);
|
||||
|
||||
struct resampler_config
|
||||
{
|
||||
resampler_config_get_float_t get_float;
|
||||
resampler_config_get_int_t get_int;
|
||||
|
||||
resampler_config_get_float_array_t get_float_array;
|
||||
resampler_config_get_int_array_t get_int_array;
|
||||
|
||||
resampler_config_get_string_t get_string;
|
||||
/* Avoid problems where resampler plug and host are
|
||||
* linked against different C runtimes. */
|
||||
resampler_config_free_t free;
|
||||
};
|
||||
|
||||
/* Bandwidth factor. Will be < 1.0 for downsampling, > 1.0 for upsampling.
|
||||
* Corresponds to expected resampling ratio. */
|
||||
typedef void *(*resampler_init_t)(const struct resampler_config *config,
|
||||
double bandwidth_mod, resampler_simd_mask_t mask);
|
||||
|
||||
/* Frees the handle. */
|
||||
typedef void (*resampler_free_t)(void *data);
|
||||
|
||||
/* Processes input data. */
|
||||
typedef void (*resampler_process_t)(void *_data, struct resampler_data *data);
|
||||
|
||||
typedef struct retro_resampler
|
||||
{
|
||||
resampler_init_t init;
|
||||
resampler_process_t process;
|
||||
resampler_free_t free;
|
||||
|
||||
/* Must be RESAMPLER_API_VERSION */
|
||||
unsigned api_version;
|
||||
|
||||
/* Human readable identifier of implementation. */
|
||||
const char *ident;
|
||||
|
||||
/* Computer-friendly short version of ident.
|
||||
* Lower case, no spaces and special characters, etc. */
|
||||
const char *short_ident;
|
||||
} retro_resampler_t;
|
||||
|
||||
typedef struct audio_frame_float
|
||||
{
|
||||
float l;
|
||||
float r;
|
||||
} audio_frame_float_t;
|
||||
|
||||
extern retro_resampler_t sinc_resampler;
|
||||
#ifdef HAVE_CC_RESAMPLER
|
||||
extern retro_resampler_t CC_resampler;
|
||||
#endif
|
||||
extern retro_resampler_t nearest_resampler;
|
||||
extern retro_resampler_t null_resampler;
|
||||
|
||||
/**
|
||||
* audio_resampler_driver_find_handle:
|
||||
* @index : 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 index);
|
||||
|
||||
/**
|
||||
* audio_resampler_driver_find_ident:
|
||||
* @index : 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 index);
|
||||
|
||||
/**
|
||||
* 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, double bw_ratio);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
55
libretro-common/include/audio/conversion/float_to_s16.h
Normal file
55
libretro-common/include/audio/conversion/float_to_s16.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (float_to_s16.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 __LIBRETRO_SDK_CONVERSION_FLOAT_TO_S16_H__
|
||||
#define __LIBRETRO_SDK_CONVERSION_FLOAT_TO_S16_H__
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
void convert_float_to_s16(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
55
libretro-common/include/audio/conversion/s16_to_float.h
Normal file
55
libretro-common/include/audio/conversion/s16_to_float.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (s16_to_float.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 __LIBRETRO_SDK_CONVERSION_S16_TO_FLOAT_H__
|
||||
#define __LIBRETRO_SDK_CONVERSION_S16_TO_FLOAT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
53
libretro-common/include/audio/dsp_filter.h
Normal file
53
libretro-common/include/audio/dsp_filter.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (dsp_filter.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 __LIBRETRO_SDK_AUDIO_DSP_FILTER_H
|
||||
#define __LIBRETRO_SDK_AUDIO_DSP_FILTER_H
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
typedef struct retro_dsp_filter retro_dsp_filter_t;
|
||||
|
||||
retro_dsp_filter_t *retro_dsp_filter_new(const char *filter_config,
|
||||
void *string_data, float sample_rate);
|
||||
|
||||
void retro_dsp_filter_free(retro_dsp_filter_t *dsp);
|
||||
|
||||
struct retro_dsp_data
|
||||
{
|
||||
float *input;
|
||||
unsigned input_frames;
|
||||
|
||||
/* Set by retro_dsp_filter_process(). */
|
||||
float *output;
|
||||
unsigned output_frames;
|
||||
};
|
||||
|
||||
void retro_dsp_filter_process(retro_dsp_filter_t *dsp,
|
||||
struct retro_dsp_data *data);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
65
libretro-common/include/clamping.h
Normal file
65
libretro-common/include/clamping.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (clamping.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 _LIBRETRO_SDK_CLAMPING_H
|
||||
#define _LIBRETRO_SDK_CLAMPING_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <retro_inline.h>
|
||||
|
||||
/**
|
||||
* clamp_float:
|
||||
* @val : initial value
|
||||
* @lower : lower limit that value should be clamped against
|
||||
* @upper : upper limit that value should be clamped against
|
||||
*
|
||||
* Clamps a floating point value.
|
||||
*
|
||||
* Returns: a clamped value of initial float value @val.
|
||||
*/
|
||||
static INLINE float clamp_float(float val, float lower, float upper)
|
||||
{
|
||||
if (val < lower)
|
||||
return lower;
|
||||
if (val > upper)
|
||||
return upper;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* clamp_8bit:
|
||||
* @val : initial value
|
||||
*
|
||||
* Clamps an unsigned 8-bit value.
|
||||
*
|
||||
* Returns: a clamped value of initial unsigned 8-bit value @val.
|
||||
*/
|
||||
static INLINE uint8_t clamp_8bit(int val)
|
||||
{
|
||||
if (val > 255)
|
||||
return 255;
|
||||
if (val < 0)
|
||||
return 0;
|
||||
return (uint8_t)val;
|
||||
}
|
||||
|
||||
#endif
|
84
libretro-common/include/compat/apple_compat.h
Normal file
84
libretro-common/include/compat/apple_compat.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (apple_compat.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 __APPLE_COMPAT_H
|
||||
#define __APPLE_COMPAT_H
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4)
|
||||
typedef int NSInteger;
|
||||
typedef unsigned NSUInteger;
|
||||
typedef float CGFloat;
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature
|
||||
/* Compatibility with non-Clang compilers. */
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef CF_RETURNS_RETAINED
|
||||
#if __has_feature(attribute_cf_returns_retained)
|
||||
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
|
||||
#else
|
||||
#define CF_RETURNS_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_INLINE
|
||||
#define NS_INLINE inline
|
||||
#endif
|
||||
|
||||
NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetainCompat(id X)
|
||||
{
|
||||
#if __has_feature(objc_arc)
|
||||
return (__bridge_retained CFTypeRef)X;
|
||||
#else
|
||||
return X;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef IOS
|
||||
#ifndef __IPHONE_5_0
|
||||
#warning "This project uses features only available in iOS SDK 5.0 and later."
|
||||
#endif
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <GLKit/GLKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef __OBJC__
|
||||
#include <objc/objc-runtime.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
30
libretro-common/include/compat/fnmatch.h
Normal file
30
libretro-common/include/compat/fnmatch.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (fnmatch.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 __LIBRETRO_SDK_COMPAT_FNMATCH_H__
|
||||
#define __LIBRETRO_SDK_COMPAT_FNMATCH_H__
|
||||
|
||||
#define FNM_NOMATCH 1
|
||||
|
||||
int rl_fnmatch(const char *pattern, const char *string, int flags);
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user