1
0
mirror of https://github.com/libretro/RetroArch synced 2024-07-05 09:48:42 +00:00

Squashed 'libretro-common/' content from commit 377cef30fa

git-subtree-dir: libretro-common
git-subtree-split: 377cef30faf410724141b280665db1b6b82cbc7b
This commit is contained in:
twinaphex 2018-05-12 17:56:33 +02:00
commit ce9ce1d353
286 changed files with 73819 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.o

318
audio/audio_mix.c Normal file
View File

@ -0,0 +1,318 @@
/* Copyright (C) 2010-2018 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;
int64_t len = 0;
void *buf = NULL;
audio_chunk_t *chunk = (audio_chunk_t*)calloc(1, sizeof(*chunk));
if (!chunk)
return NULL;
if (!filestream_read_file(path, &buf, &len))
{
printf("Could not open WAV file for reading.\n");
goto error;
}
chunk->sample_rate = sample_rate;
chunk->buf = buf;
chunk->len = len;
chunk->rwav = (rwav_t*)malloc(sizeof(rwav_t));
if (rwav_load(chunk->rwav, chunk->buf, chunk->len) == RWAV_ITERATE_ERROR)
{
printf("error: could not load WAV file\n");
goto error;
}
/* numsamples does not know or care about
* multiple channels, but we need space for 2 */
chunk->upsample_buf = (int16_t*)memalign_alloc(128,
chunk->rwav->numsamples * 2 * sizeof(int16_t));
sample_size = chunk->rwav->bitspersample / 8;
if (sample_size == 1)
{
unsigned i;
for (i = 0; i < chunk->rwav->numsamples; i++)
{
uint8_t *sample = (
(uint8_t*)chunk->rwav->samples) +
(i * chunk->rwav->numchannels);
chunk->upsample_buf[i * 2] = (int16_t)((sample[0] - 128) << 8);
if (chunk->rwav->numchannels == 1)
chunk->upsample_buf[(i * 2) + 1] = (int16_t)((sample[0] - 128) << 8);
else if (chunk->rwav->numchannels == 2)
chunk->upsample_buf[(i * 2) + 1] = (int16_t)((sample[1] - 128) << 8);
}
}
else if (sample_size == 2)
{
if (chunk->rwav->numchannels == 1)
{
unsigned i;
for (i = 0; i < chunk->rwav->numsamples; i++)
{
int16_t sample = ((int16_t*)chunk->rwav->samples)[i];
chunk->upsample_buf[i * 2] = sample;
chunk->upsample_buf[(i * 2) + 1] = sample;
}
}
else if (chunk->rwav->numchannels == 2)
memcpy(chunk->upsample_buf, chunk->rwav->samples, chunk->rwav->subchunk2size);
}
else if (sample_size != 2)
{
/* we don't support any other sample size besides 8 and 16-bit yet */
printf("error: we don't support a sample size of %d\n", sample_size);
goto error;
}
if (sample_rate != (int)chunk->rwav->samplerate)
{
chunk->resample = true;
chunk->ratio = (double)sample_rate / chunk->rwav->samplerate;
retro_resampler_realloc(&chunk->resampler_data,
&chunk->resampler,
NULL,
RESAMPLER_QUALITY_DONTCARE,
chunk->ratio);
if (chunk->resampler && chunk->resampler_data)
{
struct resampler_data info;
chunk->float_buf = (float*)memalign_alloc(128, chunk->rwav->numsamples * 2 * chunk->ratio * sizeof(float));
/* why is *3 needed instead of just *2? does the sinc driver require more space than we know about? */
chunk->float_resample_buf = (float*)memalign_alloc(128, chunk->rwav->numsamples * 3 * chunk->ratio * sizeof(float));
convert_s16_to_float(chunk->float_buf, chunk->upsample_buf, chunk->rwav->numsamples * 2, 1.0);
info.data_in = (const float*)chunk->float_buf;
info.data_out = chunk->float_resample_buf;
/* a 'frame' consists of two channels, so we set this
* to the number of samples irrespective of channel count */
info.input_frames = chunk->rwav->numsamples;
info.output_frames = 0;
info.ratio = chunk->ratio;
chunk->resampler->process(chunk->resampler_data, &info);
/* number of output_frames does not increase with multiple channels, but assume we need space for 2 */
chunk->resample_buf = (int16_t*)memalign_alloc(128, info.output_frames * 2 * sizeof(int16_t));
chunk->resample_len = info.output_frames;
convert_float_to_s16(chunk->resample_buf, chunk->float_resample_buf, info.output_frames * 2);
}
}
return chunk;
error:
audio_mix_free_chunk(chunk);
return NULL;
}
size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk)
{
if (!chunk)
return 0;
if (chunk->rwav)
{
if (chunk->resample)
return chunk->resample_len;
return chunk->rwav->numsamples;
}
/* no other filetypes supported yet */
return 0;
}
/**
* audio_mix_get_chunk_sample:
* @chunk : audio chunk instance
* @channel : channel of the sample (0=left, 1=right)
* @index : index of the sample
*
* Get a sample from an audio chunk.
*
* Returns: A signed 16-bit audio sample.
**/
int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk, unsigned channel, size_t index)
{
if (!chunk)
return 0;
if (chunk->rwav)
{
int sample_size = chunk->rwav->bitspersample / 8;
int16_t sample_out = 0;
/* 0 is the first/left channel */
uint8_t *sample = NULL;
if (chunk->resample)
sample = (uint8_t*)chunk->resample_buf +
(sample_size * index * chunk->rwav->numchannels) + (channel * sample_size);
else
sample = (uint8_t*)chunk->upsample_buf +
(sample_size * index * chunk->rwav->numchannels) + (channel * sample_size);
sample_out = (int16_t)*sample;
return sample_out;
}
/* no other filetypes supported yet */
return 0;
}
int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk)
{
if (!chunk)
return 0;
if (chunk->rwav)
{
int16_t *sample;
if (chunk->resample)
sample = chunk->resample_buf;
else
sample = chunk->upsample_buf;
return sample;
}
return NULL;
}
int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk)
{
if (!chunk)
return 0;
if (chunk->rwav)
return chunk->rwav->numchannels;
/* don't support other formats yet */
return 0;
}

1254
audio/audio_mixer.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,160 @@
/* Copyright (C) 2010-2018 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
}

View File

@ -0,0 +1,69 @@
/* Copyright (C) 2010-2018 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

View File

@ -0,0 +1,64 @@
/* Copyright (C) 2010-2018 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

View File

@ -0,0 +1,189 @@
/* Copyright (C) 2010-2018 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
}

View File

@ -0,0 +1,76 @@
/* Copyright (C) 2010-2018 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

View File

@ -0,0 +1,71 @@
/* Copyright (C) 2010-2018 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

325
audio/dsp_filter.c Normal file
View File

@ -0,0 +1,325 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (dsp_filter.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <retro_miscellaneous.h>
#include <compat/posix_string.h>
#include <dynamic/dylib.h>
#include <file/file_path.h>
#include <file/config_file_userdata.h>
#include <features/features_cpu.h>
#include <lists/string_list.h>
#include <string/stdstring.h>
#include <libretro_dspfilter.h>
#include <audio/dsp_filter.h>
struct retro_dsp_plug
{
#ifdef HAVE_DYLIB
dylib_t lib;
#endif
const struct dspfilter_implementation *impl;
};
struct retro_dsp_instance
{
const struct dspfilter_implementation *impl;
void *impl_data;
};
struct retro_dsp_filter
{
config_file_t *conf;
struct retro_dsp_plug *plugs;
unsigned num_plugs;
struct retro_dsp_instance *instances;
unsigned num_instances;
};
static const struct dspfilter_implementation *find_implementation(
retro_dsp_filter_t *dsp, const char *ident)
{
unsigned i;
for (i = 0; i < dsp->num_plugs; i++)
{
if (string_is_equal(dsp->plugs[i].impl->short_ident, ident))
return dsp->plugs[i].impl;
}
return NULL;
}
static const struct dspfilter_config dspfilter_config = {
config_userdata_get_float,
config_userdata_get_int,
config_userdata_get_float_array,
config_userdata_get_int_array,
config_userdata_get_string,
config_userdata_free,
};
static bool create_filter_graph(retro_dsp_filter_t *dsp, float sample_rate)
{
unsigned i;
struct retro_dsp_instance *instances = NULL;
unsigned filters = 0;
if (!config_get_uint(dsp->conf, "filters", &filters))
return false;
instances = (struct retro_dsp_instance*)calloc(filters, sizeof(*instances));
if (!instances)
return false;
dsp->instances = instances;
dsp->num_instances = filters;
for (i = 0; i < filters; i++)
{
struct config_file_userdata userdata;
struct dspfilter_info info;
char key[64];
char name[64];
key[0] = name[0] = '\0';
info.input_rate = sample_rate;
snprintf(key, sizeof(key), "filter%u", i);
if (!config_get_array(dsp->conf, key, name, sizeof(name)))
return false;
dsp->instances[i].impl = find_implementation(dsp, name);
if (!dsp->instances[i].impl)
return false;
userdata.conf = dsp->conf;
/* Index-specific configs take priority over ident-specific. */
userdata.prefix[0] = key;
userdata.prefix[1] = dsp->instances[i].impl->short_ident;
dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info,
&dspfilter_config, &userdata);
if (!dsp->instances[i].impl_data)
return false;
}
return true;
}
#if defined(HAVE_FILTERS_BUILTIN)
extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
static const dspfilter_get_implementation_t dsp_plugs_builtin[] = {
panning_dspfilter_get_implementation,
iir_dspfilter_get_implementation,
echo_dspfilter_get_implementation,
phaser_dspfilter_get_implementation,
wahwah_dspfilter_get_implementation,
eq_dspfilter_get_implementation,
chorus_dspfilter_get_implementation,
};
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
{
unsigned i;
dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
struct retro_dsp_plug *plugs = (struct retro_dsp_plug*)
calloc(ARRAY_SIZE(dsp_plugs_builtin), sizeof(*plugs));
if (!plugs)
return false;
dsp->plugs = plugs;
dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin);
for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++)
{
dsp->plugs[i].impl = dsp_plugs_builtin[i](mask);
if (!dsp->plugs[i].impl)
return false;
}
return true;
}
#elif defined(HAVE_DYLIB)
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
{
unsigned i;
dspfilter_simd_mask_t mask = cpu_features_get();
unsigned list_size = list ? list->size : 0;
for (i = 0; i < list_size; i++)
{
dspfilter_get_implementation_t cb;
const struct dspfilter_implementation *impl = NULL;
struct retro_dsp_plug *new_plugs = NULL;
dylib_t lib =
dylib_load(list->elems[i].data);
if (!lib)
continue;
cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation");
if (!cb)
{
dylib_close(lib);
continue;
}
impl = cb(mask);
if (!impl)
{
dylib_close(lib);
continue;
}
if (impl->api_version != DSPFILTER_API_VERSION)
{
dylib_close(lib);
continue;
}
new_plugs = (struct retro_dsp_plug*)
realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1));
if (!new_plugs)
{
dylib_close(lib);
return false;
}
/* Found plug. */
dsp->plugs = new_plugs;
dsp->plugs[dsp->num_plugs].lib = lib;
dsp->plugs[dsp->num_plugs].impl = impl;
dsp->num_plugs++;
}
return true;
}
#endif
retro_dsp_filter_t *retro_dsp_filter_new(
const char *filter_config,
void *string_data,
float sample_rate)
{
config_file_t *conf = NULL;
struct string_list *plugs = NULL;
retro_dsp_filter_t *dsp = (retro_dsp_filter_t*)calloc(1, sizeof(*dsp));
if (!dsp)
return NULL;
conf = config_file_new(filter_config);
if (!conf) /* Did not find config. */
goto error;
dsp->conf = conf;
if (string_data)
plugs = (struct string_list*)string_data;
#if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN)
if (!append_plugs(dsp, plugs))
goto error;
#endif
if (plugs)
string_list_free(plugs);
plugs = NULL;
if (!create_filter_graph(dsp, sample_rate))
goto error;
return dsp;
error:
if (plugs)
string_list_free(plugs);
retro_dsp_filter_free(dsp);
return NULL;
}
void retro_dsp_filter_free(retro_dsp_filter_t *dsp)
{
unsigned i;
if (!dsp)
return;
for (i = 0; i < dsp->num_instances; i++)
{
if (dsp->instances[i].impl_data && dsp->instances[i].impl)
dsp->instances[i].impl->free(dsp->instances[i].impl_data);
}
free(dsp->instances);
#ifdef HAVE_DYLIB
for (i = 0; i < dsp->num_plugs; i++)
{
if (dsp->plugs[i].lib)
dylib_close(dsp->plugs[i].lib);
}
free(dsp->plugs);
#endif
if (dsp->conf)
config_file_free(dsp->conf);
free(dsp);
}
void retro_dsp_filter_process(retro_dsp_filter_t *dsp,
struct retro_dsp_data *data)
{
unsigned i;
struct dspfilter_output output = {0};
struct dspfilter_input input = {0};
output.samples = data->input;
output.frames = data->input_frames;
for (i = 0; i < dsp->num_instances; i++)
{
input.samples = output.samples;
input.frames = output.frames;
dsp->instances[i].impl->process(
dsp->instances[i].impl_data, &output, &input);
}
data->output = output.samples;
data->output_frames = output.frames;
}

View 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"

View 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"

View 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

View File

@ -0,0 +1,4 @@
filters = 1
filter0 = crystalizer
# Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max.
crystalizer_intensity = 5.0

41
audio/dsp_filters/EQ.dsp Normal file
View 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

View 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"

View 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

View File

@ -0,0 +1,7 @@
filters = 1
filter0 = iir
iir_gain = -12.0
iir_type = HSH
iir_frequency = 8000.0

23
audio/dsp_filters/IIR.dsp Normal file
View 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

View 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

100
audio/dsp_filters/Makefile Normal file
View File

@ -0,0 +1,100 @@
compiler := gcc
extra_flags :=
use_neon := 0
build = release
DYLIB := so
PREFIX := /usr
INSTALLDIR := $(PREFIX)/lib/retroarch/filters/audio
ifeq ($(platform),)
platform = unix
ifeq ($(shell uname -a),)
platform = win
else ifneq ($(findstring MINGW,$(shell uname -a)),)
platform = win
else ifneq ($(findstring Darwin,$(shell uname -a)),)
platform = osx
arch = intel
ifeq ($(shell uname -p),powerpc)
arch = ppc
endif
else ifneq ($(findstring win,$(shell uname -a)),)
platform = win
endif
endif
ifeq ($(platform),gcc)
extra_rules_gcc := $(shell $(compiler) -dumpmachine)
endif
ifneq (,$(findstring armv7,$(extra_rules_gcc)))
extra_flags += -mcpu=cortex-a9 -mtune=cortex-a9 -mfpu=neon
use_neon := 1
endif
ifneq (,$(findstring hardfloat,$(extra_rules_gcc)))
extra_flags += -mfloat-abi=hard
endif
ifeq (release,$(build))
extra_flags += -O2
endif
ifeq (debug,$(build))
extra_flags += -O0 -g
endif
ldflags := $(LDFLAGS) -shared -lm -Wl,--version-script=link.T
ifeq ($(platform), unix)
DYLIB = so
else ifeq ($(platform), osx)
compiler := $(CC)
DYLIB = dylib
ldflags := -dynamiclib
else
extra_flags += -static-libgcc -static-libstdc++
DYLIB = dll
endif
CC := $(compiler) -Wall
CXX := $(subst CC,++,$(compiler)) -std=gnu++0x -Wall
flags := $(CPPFLAGS) $(CFLAGS) -fPIC $(extra_flags) -I../../include
asflags := $(ASFLAGS) -fPIC $(extra_flags)
objects :=
ifeq (1,$(use_neon))
ASMFLAGS := -INEON/asm
asflags += -mfpu=neon
endif
plugs := $(wildcard *.c)
objects := $(plugs:.c=.o)
targets := $(objects:.o=.$(DYLIB))
all: build;
%.o: %.S
$(CC) -c -o $@ $(asflags) $(ASMFLAGS) $<
%.o: %.c
$(CC) -c -o $@ $(flags) $<
%.$(DYLIB): %.o
$(CC) -o $@ $(ldflags) $(flags) $^
build: $(targets)
clean:
rm -f *.o
rm -f *.$(DYLIB)
strip:
strip -s *.$(DYLIB)
install:
mkdir -p $(DESTDIR)$(INSTALLDIR)
cp -t $(DESTDIR)$(INSTALLDIR) $(targets) *.dsp
test-install:
DESTDIR=/tmp/build $(MAKE) install

View 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"

View 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"

View 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

View 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

View File

@ -0,0 +1,7 @@
filters = 1
filter0 = tremolo
# Defaults.
#tremolo_frequency = 4.0
#tremolo_depth = 0.9

View File

@ -0,0 +1,7 @@
filters = 1
filter0 = vibrato
# Defaults.
#vibrato_frequency = 5.0
#vibrato_depth = 0.5

View 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
audio/dsp_filters/chorus.c Normal file
View 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

View File

@ -0,0 +1,93 @@
/* Copyright (C) 2010-2017 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (echo.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
struct delta_data
{
float intensity;
float old[2];
};
static void delta_free(void *data)
{
free(data);
}
static void delta_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i, c;
struct delta_data *d = (struct delta_data*)data;
float *out = output->samples;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++)
{
for (c = 0; c < 2; c++)
{
float current = *out;
*out++ = current + (current - d->old[c]) * d->intensity;
d->old[c] = current;
}
}
}
static void *delta_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
struct delta_data *d = (struct delta_data*)calloc(1, sizeof(*d));
if (!d)
return NULL;
config->get_float(userdata, "intensity", &d->intensity, 5.0f);
return d;
}
static const struct dspfilter_implementation delta_plug = {
delta_init,
delta_process,
delta_free,
DSPFILTER_API_VERSION,
"Delta Sharpening",
"crystalizer",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation delta_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &delta_plug;
}
#undef dspfilter_get_implementation

182
audio/dsp_filters/echo.c Normal file
View 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
audio/dsp_filters/eq.c Normal file
View 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
audio/dsp_filters/fft/fft.c Normal file
View File

@ -0,0 +1,205 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fft.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include "fft.h"
#include <retro_miscellaneous.h>
struct fft
{
fft_complex_t *interleave_buffer;
fft_complex_t *phase_lut;
unsigned *bitinverse_buffer;
unsigned size;
};
static unsigned bitswap(unsigned x, unsigned size_log2)
{
unsigned i;
unsigned ret = 0;
for (i = 0; i < size_log2; i++)
ret |= ((x >> i) & 1) << (size_log2 - i - 1);
return ret;
}
static void build_bitinverse(unsigned *bitinverse, unsigned size_log2)
{
unsigned i;
unsigned size = 1 << size_log2;
for (i = 0; i < size; i++)
bitinverse[i] = bitswap(i, size_log2);
}
static fft_complex_t exp_imag(double phase)
{
fft_complex_t out = { cos(phase), sin(phase) };
return out;
}
static void build_phase_lut(fft_complex_t *out, int size)
{
int i;
out += size;
for (i = -size; i <= size; i++)
out[i] = exp_imag((M_PI * i) / size);
}
static void interleave_complex(const unsigned *bitinverse,
fft_complex_t *out, const fft_complex_t *in,
unsigned samples, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in += step)
out[bitinverse[i]] = *in;
}
static void interleave_float(const unsigned *bitinverse,
fft_complex_t *out, const float *in,
unsigned samples, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in += step)
{
unsigned inv_i = bitinverse[i];
out[inv_i].real = *in;
out[inv_i].imag = 0.0f;
}
}
static void resolve_float(float *out, const fft_complex_t *in, unsigned samples,
float gain, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in++, out += step)
*out = gain * in->real;
}
fft_t *fft_new(unsigned block_size_log2)
{
unsigned size;
fft_t *fft = (fft_t*)calloc(1, sizeof(*fft));
if (!fft)
return NULL;
size = 1 << block_size_log2;
fft->interleave_buffer = (fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer));
fft->bitinverse_buffer = (unsigned*)calloc(size, sizeof(*fft->bitinverse_buffer));
fft->phase_lut = (fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut));
if (!fft->interleave_buffer || !fft->bitinverse_buffer || !fft->phase_lut)
goto error;
fft->size = size;
build_bitinverse(fft->bitinverse_buffer, block_size_log2);
build_phase_lut(fft->phase_lut, size);
return fft;
error:
fft_free(fft);
return NULL;
}
void fft_free(fft_t *fft)
{
if (!fft)
return;
free(fft->interleave_buffer);
free(fft->bitinverse_buffer);
free(fft->phase_lut);
free(fft);
}
static void butterfly(fft_complex_t *a, fft_complex_t *b, fft_complex_t mod)
{
mod = fft_complex_mul(mod, *b);
*b = fft_complex_sub(*a, mod);
*a = fft_complex_add(*a, mod);
}
static void butterflies(fft_complex_t *butterfly_buf,
const fft_complex_t *phase_lut,
int phase_dir, unsigned step_size, unsigned samples)
{
unsigned i, j;
for (i = 0; i < samples; i += step_size << 1)
{
int phase_step = (int)samples * phase_dir / (int)step_size;
for (j = i; j < i + step_size; j++)
butterfly(&butterfly_buf[j], &butterfly_buf[j + step_size],
phase_lut[phase_step * (int)(j - i)]);
}
}
void fft_process_forward_complex(fft_t *fft,
fft_complex_t *out, const fft_complex_t *in, unsigned step)
{
unsigned step_size;
unsigned samples = fft->size;
interleave_complex(fft->bitinverse_buffer, out, in, samples, step);
for (step_size = 1; step_size < samples; step_size <<= 1)
{
butterflies(out,
fft->phase_lut + samples,
-1, step_size, samples);
}
}
void fft_process_forward(fft_t *fft,
fft_complex_t *out, const float *in, unsigned step)
{
unsigned step_size;
unsigned samples = fft->size;
interleave_float(fft->bitinverse_buffer, out, in, samples, step);
for (step_size = 1; step_size < fft->size; step_size <<= 1)
{
butterflies(out,
fft->phase_lut + samples,
-1, step_size, samples);
}
}
void fft_process_inverse(fft_t *fft,
float *out, const fft_complex_t *in, unsigned step)
{
unsigned step_size;
unsigned samples = fft->size;
interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer,
in, samples, 1);
for (step_size = 1; step_size < samples; step_size <<= 1)
{
butterflies(fft->interleave_buffer,
fft->phase_lut + samples,
1, step_size, samples);
}
resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples, step);
}

View File

@ -0,0 +1,46 @@
/* Copyright (C) 2010-2018 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
audio/dsp_filters/iir.c Normal file
View 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
audio/dsp_filters/link.T Normal file
View File

@ -0,0 +1,4 @@
{
global: dspfilter_get_implementation;
local: *;
};

112
audio/dsp_filters/panning.c Normal file
View 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
audio/dsp_filters/phaser.c Normal file
View 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
audio/dsp_filters/reverb.c Normal file
View 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;
struct revmodel
{
struct comb combL[numcombs];
struct allpass allpassL[numallpasses];
float **bufcomb;
float **bufallpass;
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,int srate)
{
static const int comb_lengths[8] = { 1116,1188,1277,1356,1422,1491,1557,1617 };
static const int allpass_lengths[4] = { 225,341,441,556 };
double r = srate * (1 / 44100.0);
unsigned c;
rev->bufcomb=malloc(numcombs*sizeof(float*));
for (c = 0; c < numcombs; ++c)
{
rev->bufcomb[c] = malloc(r*comb_lengths[c]*sizeof(float));
rev->combL[c].buffer = rev->bufcomb[c];
rev->combL[c].bufsize=r*comb_lengths[c];
}
rev->bufallpass=malloc(numallpasses*sizeof(float*));
for (c = 0; c < numallpasses; ++c)
{
rev->bufallpass[c] = malloc(r*allpass_lengths[c]*sizeof(float));
rev->allpassL[c].buffer = rev->bufallpass[c];
rev->allpassL[c].bufsize=r*allpass_lengths[c];
}
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)
{
struct reverb_data *rev = (struct reverb_data*)data;
unsigned i;
for (i = 0; i < numcombs; i++) {
free(rev->left.bufcomb[i]);
free(rev->right.bufcomb[i]);
}
free(rev->left.bufcomb);
free(rev->right.bufcomb);
for (i = 0; i < numallpasses; i++) {
free(rev->left.bufallpass[i]);
free(rev->right.bufallpass[i]);
}
free(rev->left.bufallpass);
free(rev->right.bufallpass);
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,info->input_rate);
revmodel_init(&rev->right,info->input_rate);
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

133
audio/dsp_filters/tremolo.c Normal file
View File

@ -0,0 +1,133 @@
/* Copyright (C) 2010-2017 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (tremolo.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))
struct tremolo_core
{
float freq;
float depth;
float* wavetable;
int index;
int maxindex;
};
struct tremolo
{
struct tremolo_core left, right;
};
static void tremolo_free(void *data)
{
struct tremolo *tre = (struct tremolo*)data;
free(tre->left.wavetable);
free(tre->right.wavetable);
free(data);
}
static void tremolocore_init(struct tremolo_core *core,float depth,int samplerate,float freq)
{
const double offset = 1. - depth / 2.;
unsigned i;
double env;
core->index = 0;
core->maxindex = samplerate/freq;
core->wavetable = malloc(core->maxindex*sizeof(float));
memset(core->wavetable, 0, core->maxindex * sizeof(float));
for (i = 0; i < core->maxindex; i++) {
env = freq * i / samplerate;
env = sin((M_PI*2) * fmod(env + 0.25, 1.0));
core->wavetable[i] = env * (1 - fabs(offset)) + offset;
}
}
float tremolocore_core(struct tremolo_core *core,float in)
{
core->index = core->index % core->maxindex;
return in * core->wavetable[core->index++];
}
static void tremolo_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
float *out;
struct tremolo *tre = (struct tremolo*)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] = tremolocore_core(&tre->left, in[0]);
out[1] = tremolocore_core(&tre->right, in[1]);
}
}
static void *tremolo_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float freq, depth;
struct tremolo *tre = (struct tremolo*)calloc(1, sizeof(*tre));
if (!tre)
return NULL;
config->get_float(userdata, "freq", &freq,4.0f);
config->get_float(userdata, "depth", &depth, 0.9f);
tremolocore_init(&tre->left,depth,info->input_rate,freq);
tremolocore_init(&tre->right,depth,info->input_rate,freq);
return tre;
}
static const struct dspfilter_implementation tremolo_plug = {
tremolo_init,
tremolo_process,
tremolo_free,
DSPFILTER_API_VERSION,
"Tremolo",
"tremolo",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation tremolo_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &tremolo_plug;
}
#undef dspfilter_get_implementation

169
audio/dsp_filters/vibrato.c Normal file
View File

@ -0,0 +1,169 @@
/* Copyright (C) 2010-2017 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (vibrato.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))
const float BASE_DELAY_SEC = 0.002; // 2 ms
const float VIBRATO_FREQUENCY_DEFAULT_HZ = 2;
const float VIBRATO_FREQUENCY_MAX_HZ = 14;
const float VIBRATO_DEPTH_DEFAULT_PERCENT = 50;
const int add_delay = 3;
float hermite_interp(float x, float *y)
{
float c0, c1, c2, c3;
c0 = y[1];
c1 = (1.0 / 2.0)*(y[2] - y[0]);
c2 = (y[0] - (5.0 / 2.0)*y[1]) + (2.0*y[2] - (1.0 / 2.0)*y[3]);
c3 = (1.0 / 2.0)*(y[3] - y[0]) + (3.0 / 2.0)*(y[1] - y[2]);
return ((c3*x + c2)*x + c1)*x + c0;
}
struct vibrato_core
{
float freq;
float samplerate;
int phase;
float depth;
float* buffer;
int writeindex;
int size;
};
struct vibrato
{
struct vibrato_core left, right;
};
static void vibrato_free(void *data)
{
struct vibrato *vib = (struct vibrato*)data;
free(vib->left.buffer);
free(vib->right.buffer);
free(data);
}
static void vibratocore_init(struct vibrato_core *core,float depth,int samplerate,float freq)
{
core->size = BASE_DELAY_SEC * samplerate * 2;
core->buffer = malloc((core->size + add_delay)*sizeof(float));
memset(core->buffer, 0, (core->size + add_delay) * sizeof(float));
core->samplerate = samplerate;
core->freq = freq;
core->depth = depth;
core->phase = 0;
core->writeindex = 0;
}
float vibratocore_core(struct vibrato_core *core,float in)
{
float M = core->freq / core->samplerate;
int maxphase = core->samplerate / core->freq;
float lfo = sin(M * 2. * M_PI * core->phase++);
core->phase = core->phase % maxphase;
lfo = (lfo + 1) * 1.; // transform from [-1; 1] to [0; 1]
int maxdelay = BASE_DELAY_SEC * core->samplerate;
float delay = lfo * core->depth * maxdelay;
delay += add_delay;
float readindex = core->writeindex - 1 - delay;
while (readindex < 0)readindex += core->size;
while (readindex >= core->size)readindex -= core->size;
int ipart = (int)readindex; // integer part of the delay
float fpart = readindex - ipart; // fractional part of the delay
float value = hermite_interp(fpart, &(core->buffer[ipart]));
core->buffer[core->writeindex] = in;
if (core->writeindex < add_delay){
core->buffer[core->size + core->writeindex] = in;
}
core->writeindex++;
if (core->writeindex == core->size) {
core->writeindex = 0;
}
return value;
}
static void vibrato_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
float *out;
struct vibrato *vib = (struct vibrato*)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] = vibratocore_core(&vib->left, in[0]);
out[1] = vibratocore_core(&vib->right, in[1]);
}
}
static void *vibrato_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float freq, depth;
struct vibrato *vib = (struct vibrato*)calloc(1, sizeof(*vib));
if (!vib)
return NULL;
config->get_float(userdata, "freq", &freq,5.0f);
config->get_float(userdata, "depth", &depth, 0.5f);
vibratocore_init(&vib->left,depth,info->input_rate,freq);
vibratocore_init(&vib->right,depth,info->input_rate,freq);
return vib;
}
static const struct dspfilter_implementation vibrato_plug = {
vibrato_init,
vibrato_process,
vibrato_free,
DSPFILTER_API_VERSION,
"Vibrato",
"vibrato",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation vibrato_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &vibrato_plug;
}
#undef dspfilter_get_implementation

148
audio/dsp_filters/wahwah.c Normal file
View 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

View File

@ -0,0 +1,172 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (audio_resampler.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <string/stdstring.h>
#include <features/features_cpu.h>
#include <file/config_file_userdata.h>
#include <audio/audio_resampler.h>
static const retro_resampler_t *resampler_drivers[] = {
&sinc_resampler,
#ifdef HAVE_CC_RESAMPLER
&CC_resampler,
#endif
&nearest_resampler,
&null_resampler,
NULL,
};
static const struct resampler_config resampler_config = {
config_userdata_get_float,
config_userdata_get_int,
config_userdata_get_float_array,
config_userdata_get_int_array,
config_userdata_get_string,
config_userdata_free,
};
/**
* find_resampler_driver_index:
* @ident : Identifier of resampler driver to find.
*
* Finds resampler driver index by @ident name.
*
* Returns: resampler driver index if resampler driver was found, otherwise
* -1.
**/
static int find_resampler_driver_index(const char *ident)
{
unsigned i;
for (i = 0; resampler_drivers[i]; i++)
if (string_is_equal_noncase(ident, resampler_drivers[i]->ident))
return i;
return -1;
}
/**
* audio_resampler_driver_find_handle:
* @idx : index of driver to get handle to.
*
* Returns: handle to audio resampler driver at index. Can be NULL
* if nothing found.
**/
const void *audio_resampler_driver_find_handle(int idx)
{
const void *drv = resampler_drivers[idx];
if (!drv)
return NULL;
return drv;
}
/**
* audio_resampler_driver_find_ident:
* @idx : index of driver to get handle to.
*
* Returns: Human-readable identifier of audio resampler driver at index.
* Can be NULL if nothing found.
**/
const char *audio_resampler_driver_find_ident(int idx)
{
const retro_resampler_t *drv = resampler_drivers[idx];
if (!drv)
return NULL;
return drv->ident;
}
/**
* find_resampler_driver:
* @ident : Identifier of resampler driver to find.
*
* Finds resampler by @ident name.
*
* Returns: resampler driver if resampler driver was found, otherwise
* NULL.
**/
static const retro_resampler_t *find_resampler_driver(const char *ident)
{
int i = find_resampler_driver_index(ident);
if (i >= 0)
return resampler_drivers[i];
return resampler_drivers[0];
}
/**
* resampler_append_plugs:
* @re : Resampler handle
* @backend : Resampler backend that is about to be set.
* @bw_ratio : Bandwidth ratio.
*
* Initializes resampler driver based on queried CPU features.
*
* Returns: true (1) if successfully initialized, otherwise false (0).
**/
static bool resampler_append_plugs(void **re,
const retro_resampler_t **backend,
enum resampler_quality quality,
double bw_ratio)
{
resampler_simd_mask_t mask = (resampler_simd_mask_t)cpu_features_get();
if (*backend)
*re = (*backend)->init(&resampler_config, bw_ratio, quality, mask);
if (!*re)
return false;
return true;
}
/**
* retro_resampler_realloc:
* @re : Resampler handle
* @backend : Resampler backend that is about to be set.
* @ident : Identifier name for resampler we want.
* @bw_ratio : Bandwidth ratio.
*
* Reallocates resampler. Will free previous handle before
* allocating a new one. If ident is NULL, first resampler will be used.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool retro_resampler_realloc(void **re, const retro_resampler_t **backend,
const char *ident, enum resampler_quality quality, double bw_ratio)
{
if (*re && *backend)
(*backend)->free(*re);
*re = NULL;
*backend = find_resampler_driver(ident);
if (!resampler_append_plugs(re, backend, quality, bw_ratio))
{
if (!*re)
*backend = NULL;
return false;
}
return true;
}

View File

@ -0,0 +1,90 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nearest_resampler.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <audio/audio_resampler.h>
typedef struct rarch_nearest_resampler
{
float fraction;
} rarch_nearest_resampler_t;
static void resampler_nearest_process(
void *re_, struct resampler_data *data)
{
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
audio_frame_float_t *inp = (audio_frame_float_t*)data->data_in;
audio_frame_float_t *inp_max = (audio_frame_float_t*)inp + data->input_frames;
audio_frame_float_t *outp = (audio_frame_float_t*)data->data_out;
float ratio = 1.0 / data->ratio;
while(inp != inp_max)
{
while(re->fraction > 1)
{
*outp++ = *inp;
re->fraction -= ratio;
}
re->fraction++;
inp++;
}
data->output_frames = (outp - (audio_frame_float_t*)data->data_out);
}
static void resampler_nearest_free(void *re_)
{
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
if (re)
free(re);
}
static void *resampler_nearest_init(const struct resampler_config *config,
double bandwidth_mod,
enum resampler_quality quality,
resampler_simd_mask_t mask)
{
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)
calloc(1, sizeof(rarch_nearest_resampler_t));
(void)config;
(void)mask;
if (!re)
return NULL;
re->fraction = 0;
return re;
}
retro_resampler_t nearest_resampler = {
resampler_nearest_init,
resampler_nearest_process,
resampler_nearest_free,
RESAMPLER_API_VERSION,
"nearest",
"nearest"
};

View File

@ -0,0 +1,58 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (null_resampler.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <audio/audio_resampler.h>
typedef struct rarch_null_resampler
{
void *empty;
} rarch_null_resampler_t;
static void resampler_null_process(
void *re_, struct resampler_data *data)
{
}
static void resampler_null_free(void *re_)
{
}
static void *resampler_null_init(const struct resampler_config *config,
double bandwidth_mod,
enum resampler_quality quality,
resampler_simd_mask_t mask)
{
return (void*)0;
}
retro_resampler_t null_resampler = {
resampler_null_init,
resampler_null_process,
resampler_null_free,
RESAMPLER_API_VERSION,
"null",
"null"
};

View File

@ -0,0 +1,720 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (sinc_resampler.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Bog-standard windowed SINC implementation. */
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <retro_inline.h>
#include <filters.h>
#include <memalign.h>
#include <audio/audio_resampler.h>
#include <filters.h>
#ifdef __SSE__
#include <xmmintrin.h>
#endif
#if defined(__AVX__)
#include <immintrin.h>
#endif
/* Rough SNR values for upsampling:
* LOWEST: 40 dB
* LOWER: 55 dB
* NORMAL: 70 dB
* HIGHER: 110 dB
* HIGHEST: 140 dB
*/
/* TODO, make all this more configurable. */
enum sinc_window
{
SINC_WINDOW_NONE = 0,
SINC_WINDOW_KAISER,
SINC_WINDOW_LANCZOS
};
/* For the little amount of taps we're using,
* SSE1 is faster than AVX for some reason.
* AVX code is kept here though as by increasing number
* of sinc taps, the AVX code is clearly faster than SSE1.
*/
typedef struct rarch_sinc_resampler
{
unsigned enable_avx;
unsigned phase_bits;
unsigned subphase_bits;
unsigned subphase_mask;
unsigned taps;
unsigned ptr;
uint32_t time;
float subphase_mod;
float kaiser_beta;
enum sinc_window window_type;
/* A buffer for phase_table, buffer_l and buffer_r
* are created in a single calloc().
* Ensure that we get as good cache locality as we can hope for. */
float *main_buffer;
float *phase_table;
float *buffer_l;
float *buffer_r;
} rarch_sinc_resampler_t;
#if defined(__ARM_NEON__)
#if TARGET_OS_IPHONE
#else
#define WANT_NEON
#endif
#endif
#ifdef WANT_NEON
/* Assumes that taps >= 8, and that taps is a multiple of 8. */
void process_sinc_neon_asm(float *out, const float *left,
const float *right, const float *coeff, unsigned taps);
static void resampler_sinc_process_neon(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
uint32_t ratio = phases / data->ratio;
const float *input = data->data_in;
float *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
while (frames)
{
while (frames && resamp->time >= phases)
{
/* Push in reverse to make filter more obvious. */
if (!resamp->ptr)
resamp->ptr = resamp->taps;
resamp->ptr--;
resamp->buffer_l[resamp->ptr + resamp->taps] =
resamp->buffer_l[resamp->ptr] = *input++;
resamp->buffer_r[resamp->ptr + resamp->taps] =
resamp->buffer_r[resamp->ptr] = *input++;
resamp->time -= phases;
frames--;
}
while (resamp->time < phases)
{
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
unsigned taps = resamp->taps;
unsigned phase = resamp->time >> resamp->subphase_bits;
const float *phase_table = resamp->phase_table + phase * taps;
process_sinc_neon_asm(output, buffer_l, buffer_r, phase_table, taps);
output += 2;
out_frames++;
resamp->time += ratio;
}
}
data->output_frames = out_frames;
}
#endif
#if defined(__AVX__)
static void resampler_sinc_process_avx(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
uint32_t ratio = phases / data->ratio;
const float *input = data->data_in;
float *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
while (frames)
{
while (frames && resamp->time >= phases)
{
/* Push in reverse to make filter more obvious. */
if (!resamp->ptr)
resamp->ptr = resamp->taps;
resamp->ptr--;
resamp->buffer_l[resamp->ptr + resamp->taps] =
resamp->buffer_l[resamp->ptr] = *input++;
resamp->buffer_r[resamp->ptr + resamp->taps] =
resamp->buffer_r[resamp->ptr] = *input++;
resamp->time -= phases;
frames--;
}
while (resamp->time < phases)
{
unsigned i;
__m256 delta, sum_l, sum_r;
float *delta_table = NULL;
float *phase_table = NULL;
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
unsigned taps = resamp->taps;
unsigned phase = resamp->time >> resamp->subphase_bits;
phase_table = resamp->phase_table + phase * taps;
if (resamp->window_type == SINC_WINDOW_KAISER)
{
phase_table = resamp->phase_table + phase * taps * 2;
delta_table = phase_table + taps;
delta = _mm256_set1_ps((float)
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod);
}
sum_l = _mm256_setzero_ps();
sum_r = _mm256_setzero_ps();
for (i = 0; i < taps; i += 8)
{
__m256 sinc;
__m256 buf_l = _mm256_loadu_ps(buffer_l + i);
__m256 buf_r = _mm256_loadu_ps(buffer_r + i);
if (resamp->window_type == SINC_WINDOW_KAISER)
{
__m256 deltas = _mm256_load_ps(delta_table + i);
sinc = _mm256_add_ps(_mm256_load_ps((const float*)phase_table + i),
_mm256_mul_ps(deltas, delta));
}
else
{
sinc = _mm256_load_ps((const float*)phase_table + i);
}
sum_l = _mm256_add_ps(sum_l, _mm256_mul_ps(buf_l, sinc));
sum_r = _mm256_add_ps(sum_r, _mm256_mul_ps(buf_r, sinc));
}
/* hadd on AVX is weird, and acts on low-lanes
* and high-lanes separately. */
__m256 res_l = _mm256_hadd_ps(sum_l, sum_l);
__m256 res_r = _mm256_hadd_ps(sum_r, sum_r);
res_l = _mm256_hadd_ps(res_l, res_l);
res_r = _mm256_hadd_ps(res_r, res_r);
res_l = _mm256_add_ps(_mm256_permute2f128_ps(res_l, res_l, 1), res_l);
res_r = _mm256_add_ps(_mm256_permute2f128_ps(res_r, res_r, 1), res_r);
/* This is optimized to mov %xmmN, [mem].
* There doesn't seem to be any _mm256_store_ss intrinsic. */
_mm_store_ss(output + 0, _mm256_extractf128_ps(res_l, 0));
_mm_store_ss(output + 1, _mm256_extractf128_ps(res_r, 0));
output += 2;
out_frames++;
resamp->time += ratio;
}
}
data->output_frames = out_frames;
}
#endif
#if defined(__SSE__)
static void resampler_sinc_process_sse(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
uint32_t ratio = phases / data->ratio;
const float *input = data->data_in;
float *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
while (frames)
{
while (frames && resamp->time >= phases)
{
/* Push in reverse to make filter more obvious. */
if (!resamp->ptr)
resamp->ptr = resamp->taps;
resamp->ptr--;
resamp->buffer_l[resamp->ptr + resamp->taps] =
resamp->buffer_l[resamp->ptr] = *input++;
resamp->buffer_r[resamp->ptr + resamp->taps] =
resamp->buffer_r[resamp->ptr] = *input++;
resamp->time -= phases;
frames--;
}
while (resamp->time < phases)
{
unsigned i;
__m128 sum, sum_l, sum_r, delta;
float *phase_table = NULL;
float *delta_table = NULL;
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
unsigned taps = resamp->taps;
unsigned phase = resamp->time >> resamp->subphase_bits;
if (resamp->window_type == SINC_WINDOW_KAISER)
{
phase_table = resamp->phase_table + phase * taps * 2;
delta_table = phase_table + taps;
delta = _mm_set1_ps((float)
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod);
}
else
{
phase_table = resamp->phase_table + phase * taps;
}
sum_l = _mm_setzero_ps();
sum_r = _mm_setzero_ps();
for (i = 0; i < taps; i += 4)
{
__m128 deltas, _sinc;
__m128 buf_l = _mm_loadu_ps(buffer_l + i);
__m128 buf_r = _mm_loadu_ps(buffer_r + i);
if (resamp->window_type == SINC_WINDOW_KAISER)
{
deltas = _mm_load_ps(delta_table + i);
_sinc = _mm_add_ps(_mm_load_ps((const float*)phase_table + i),
_mm_mul_ps(deltas, delta));
}
else
{
_sinc = _mm_load_ps((const float*)phase_table + i);
}
sum_l = _mm_add_ps(sum_l, _mm_mul_ps(buf_l, _sinc));
sum_r = _mm_add_ps(sum_r, _mm_mul_ps(buf_r, _sinc));
}
/* Them annoying shuffles.
* sum_l = { l3, l2, l1, l0 }
* sum_r = { r3, r2, r1, r0 }
*/
sum = _mm_add_ps(_mm_shuffle_ps(sum_l, sum_r,
_MM_SHUFFLE(1, 0, 1, 0)),
_mm_shuffle_ps(sum_l, sum_r, _MM_SHUFFLE(3, 2, 3, 2)));
/* sum = { r1, r0, l1, l0 } + { r3, r2, l3, l2 }
* sum = { R1, R0, L1, L0 }
*/
sum = _mm_add_ps(_mm_shuffle_ps(sum, sum, _MM_SHUFFLE(3, 3, 1, 1)), sum);
/* sum = {R1, R1, L1, L1 } + { R1, R0, L1, L0 }
* sum = { X, R, X, L }
*/
/* Store L */
_mm_store_ss(output + 0, sum);
/* movehl { X, R, X, L } == { X, R, X, R } */
_mm_store_ss(output + 1, _mm_movehl_ps(sum, sum));
output += 2;
out_frames++;
resamp->time += ratio;
}
}
data->output_frames = out_frames;
}
#endif
static void resampler_sinc_process_c(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
uint32_t ratio = phases / data->ratio;
const float *input = data->data_in;
float *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
while (frames)
{
while (frames && resamp->time >= phases)
{
/* Push in reverse to make filter more obvious. */
if (!resamp->ptr)
resamp->ptr = resamp->taps;
resamp->ptr--;
resamp->buffer_l[resamp->ptr + resamp->taps] =
resamp->buffer_l[resamp->ptr] = *input++;
resamp->buffer_r[resamp->ptr + resamp->taps] =
resamp->buffer_r[resamp->ptr] = *input++;
resamp->time -= phases;
frames--;
}
while (resamp->time < phases)
{
unsigned i;
float delta = 0.0f;
float sum_l = 0.0f;
float sum_r = 0.0f;
float *phase_table = NULL;
float *delta_table = NULL;
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
unsigned taps = resamp->taps;
unsigned phase = resamp->time >> resamp->subphase_bits;
if (resamp->window_type == SINC_WINDOW_KAISER)
{
phase_table = resamp->phase_table + phase * taps * 2;
delta_table = phase_table + taps;
delta = (float)
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod;
}
else
{
phase_table = resamp->phase_table + phase * taps;
}
for (i = 0; i < taps; i++)
{
float sinc_val = phase_table[i];
if (resamp->window_type == SINC_WINDOW_KAISER)
sinc_val = sinc_val + delta_table[i] * delta;
sum_l += buffer_l[i] * sinc_val;
sum_r += buffer_r[i] * sinc_val;
}
output[0] = sum_l;
output[1] = sum_r;
output += 2;
out_frames++;
resamp->time += ratio;
}
}
data->output_frames = out_frames;
}
static void resampler_sinc_free(void *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)data;
if (resamp)
memalign_free(resamp->main_buffer);
free(resamp);
}
static void sinc_init_table_kaiser(rarch_sinc_resampler_t *resamp,
double cutoff,
float *phase_table, int phases, int taps, bool calculate_delta)
{
int i, j;
double window_mod = kaiser_window_function(0.0, resamp->kaiser_beta); /* Need to normalize w(0) to 1.0. */
int stride = calculate_delta ? 2 : 1;
double sidelobes = taps / 2.0;
for (i = 0; i < phases; i++)
{
for (j = 0; j < taps; j++)
{
double sinc_phase;
float val;
int n = j * phases + i;
double window_phase = (double)n / (phases * taps); /* [0, 1). */
window_phase = 2.0 * window_phase - 1.0; /* [-1, 1) */
sinc_phase = sidelobes * window_phase;
val = cutoff * sinc(M_PI * sinc_phase * cutoff) *
kaiser_window_function(window_phase, resamp->kaiser_beta) / window_mod;
phase_table[i * stride * taps + j] = val;
}
}
if (calculate_delta)
{
int phase;
int p;
for (p = 0; p < phases - 1; p++)
{
for (j = 0; j < taps; j++)
{
float delta = phase_table[(p + 1) * stride * taps + j] -
phase_table[p * stride * taps + j];
phase_table[(p * stride + 1) * taps + j] = delta;
}
}
phase = phases - 1;
for (j = 0; j < taps; j++)
{
float val, delta;
double sinc_phase;
int n = j * phases + (phase + 1);
double window_phase = (double)n / (phases * taps); /* (0, 1]. */
window_phase = 2.0 * window_phase - 1.0; /* (-1, 1] */
sinc_phase = sidelobes * window_phase;
val = cutoff * sinc(M_PI * sinc_phase * cutoff) *
kaiser_window_function(window_phase, resamp->kaiser_beta) / window_mod;
delta = (val - phase_table[phase * stride * taps + j]);
phase_table[(phase * stride + 1) * taps + j] = delta;
}
}
}
static void sinc_init_table_lanczos(rarch_sinc_resampler_t *resamp, double cutoff,
float *phase_table, int phases, int taps, bool calculate_delta)
{
int i, j;
double window_mod = lanzcos_window_function(0.0); /* Need to normalize w(0) to 1.0. */
int stride = calculate_delta ? 2 : 1;
double sidelobes = taps / 2.0;
for (i = 0; i < phases; i++)
{
for (j = 0; j < taps; j++)
{
double sinc_phase;
float val;
int n = j * phases + i;
double window_phase = (double)n / (phases * taps); /* [0, 1). */
window_phase = 2.0 * window_phase - 1.0; /* [-1, 1) */
sinc_phase = sidelobes * window_phase;
val = cutoff * sinc(M_PI * sinc_phase * cutoff) *
lanzcos_window_function(window_phase) / window_mod;
phase_table[i * stride * taps + j] = val;
}
}
if (calculate_delta)
{
int phase;
int p;
for (p = 0; p < phases - 1; p++)
{
for (j = 0; j < taps; j++)
{
float delta = phase_table[(p + 1) * stride * taps + j] -
phase_table[p * stride * taps + j];
phase_table[(p * stride + 1) * taps + j] = delta;
}
}
phase = phases - 1;
for (j = 0; j < taps; j++)
{
float val, delta;
double sinc_phase;
int n = j * phases + (phase + 1);
double window_phase = (double)n / (phases * taps); /* (0, 1]. */
window_phase = 2.0 * window_phase - 1.0; /* (-1, 1] */
sinc_phase = sidelobes * window_phase;
val = cutoff * sinc(M_PI * sinc_phase * cutoff) *
lanzcos_window_function(window_phase) / window_mod;
delta = (val - phase_table[phase * stride * taps + j]);
phase_table[(phase * stride + 1) * taps + j] = delta;
}
}
}
static void *resampler_sinc_new(const struct resampler_config *config,
double bandwidth_mod, enum resampler_quality quality,
resampler_simd_mask_t mask)
{
double cutoff = 0.0;
size_t phase_elems = 0;
size_t elems = 0;
unsigned sidelobes = 0;
rarch_sinc_resampler_t *re = (rarch_sinc_resampler_t*)
calloc(1, sizeof(*re));
if (!re)
return NULL;
re->window_type = SINC_WINDOW_NONE;
switch (quality)
{
case RESAMPLER_QUALITY_LOWEST:
cutoff = 0.98;
sidelobes = 2;
re->phase_bits = 12;
re->subphase_bits = 10;
re->window_type = SINC_WINDOW_LANCZOS;
re->enable_avx = 0;
break;
case RESAMPLER_QUALITY_LOWER:
cutoff = 0.98;
sidelobes = 4;
re->phase_bits = 12;
re->subphase_bits = 10;
re->window_type = SINC_WINDOW_LANCZOS;
re->enable_avx = 0;
break;
case RESAMPLER_QUALITY_HIGHER:
cutoff = 0.90;
sidelobes = 32;
re->phase_bits = 10;
re->subphase_bits = 14;
re->window_type = SINC_WINDOW_KAISER;
re->kaiser_beta = 10.5;
re->enable_avx = 1;
break;
case RESAMPLER_QUALITY_HIGHEST:
cutoff = 0.962;
sidelobes = 128;
re->phase_bits = 10;
re->subphase_bits = 14;
re->window_type = SINC_WINDOW_KAISER;
re->kaiser_beta = 14.5;
re->enable_avx = 1;
break;
case RESAMPLER_QUALITY_NORMAL:
case RESAMPLER_QUALITY_DONTCARE:
cutoff = 0.825;
sidelobes = 8;
re->phase_bits = 8;
re->subphase_bits = 16;
re->window_type = SINC_WINDOW_KAISER;
re->kaiser_beta = 5.5;
re->enable_avx = 0;
break;
}
re->subphase_mask = (1 << re->subphase_bits) - 1;
re->subphase_mod = 1.0f / (1 << re->subphase_bits);
re->taps = sidelobes * 2;
/* Downsampling, must lower cutoff, and extend number of
* taps accordingly to keep same stopband attenuation. */
if (bandwidth_mod < 1.0)
{
cutoff *= bandwidth_mod;
re->taps = (unsigned)ceil(re->taps / bandwidth_mod);
}
/* Be SIMD-friendly. */
#if defined(__AVX__)
if (re->enable_avx)
re->taps = (re->taps + 7) & ~7;
else
#endif
{
#if defined(WANT_NEON)
re->taps = (re->taps + 7) & ~7;
#else
re->taps = (re->taps + 3) & ~3;
#endif
}
phase_elems = ((1 << re->phase_bits) * re->taps);
if (re->window_type == SINC_WINDOW_KAISER)
phase_elems = phase_elems * 2;
elems = phase_elems + 4 * re->taps;
re->main_buffer = (float*)memalign_alloc(128, sizeof(float) * elems);
if (!re->main_buffer)
goto error;
re->phase_table = re->main_buffer;
re->buffer_l = re->main_buffer + phase_elems;
re->buffer_r = re->buffer_l + 2 * re->taps;
switch (re->window_type)
{
case SINC_WINDOW_LANCZOS:
sinc_init_table_lanczos(re, cutoff, re->phase_table,
1 << re->phase_bits, re->taps, false);
break;
case SINC_WINDOW_KAISER:
sinc_init_table_kaiser(re, cutoff, re->phase_table,
1 << re->phase_bits, re->taps, true);
break;
case SINC_WINDOW_NONE:
goto error;
}
sinc_resampler.process = resampler_sinc_process_c;
if (mask & RESAMPLER_SIMD_AVX && re->enable_avx)
{
#if defined(__AVX__)
sinc_resampler.process = resampler_sinc_process_avx;
#endif
}
else if (mask & RESAMPLER_SIMD_SSE)
{
#if defined(__SSE__)
sinc_resampler.process = resampler_sinc_process_sse;
#endif
}
else if (mask & RESAMPLER_SIMD_NEON && re->window_type != SINC_WINDOW_KAISER)
{
#if defined(WANT_NEON)
sinc_resampler.process = resampler_sinc_process_neon;
#endif
}
return re;
error:
resampler_sinc_free(re);
return NULL;
}
retro_resampler_t sinc_resampler = {
resampler_sinc_new,
resampler_sinc_process_c,
resampler_sinc_free,
RESAMPLER_API_VERSION,
"sinc",
"sinc"
};
#undef WANT_NEON

View File

@ -0,0 +1,74 @@
/* Copyright (C) 2010-2018 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
compat/compat_fnmatch.c Normal file
View File

@ -0,0 +1,159 @@
/* Copyright (C) 2010-2018 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
compat/compat_getopt.c Normal file
View File

@ -0,0 +1,219 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_getopt.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <boolean.h>
#include <stddef.h>
#include <stdlib.h>
#include <retro_miscellaneous.h>
#include <compat/getopt.h>
#include <compat/strl.h>
#include <compat/strcasestr.h>
#include <compat/posix_string.h>
#include <retro_assert.h>
char *optarg;
int optind, opterr, optopt;
static bool is_short_option(const char *str)
{
return str[0] == '-' && str[1] != '-';
}
static bool is_long_option(const char *str)
{
return str[0] == '-' && str[1] == '-';
}
static int find_short_index(char * const *argv)
{
int idx;
for (idx = 0; argv[idx]; idx++)
{
if (is_short_option(argv[idx]))
return idx;
}
return -1;
}
static int find_long_index(char * const *argv)
{
int idx;
for (idx = 0; argv[idx]; idx++)
{
if (is_long_option(argv[idx]))
return idx;
}
return -1;
}
static int parse_short(const char *optstring, char * const *argv)
{
bool extra_opt, takes_arg, embedded_arg;
const char *opt = NULL;
char arg = argv[0][1];
if (arg == ':')
return '?';
opt = strchr(optstring, arg);
if (!opt)
return '?';
extra_opt = argv[0][2];
takes_arg = opt[1] == ':';
/* If we take an argument, and we see additional characters,
* this is in fact the argument (i.e. -cfoo is same as -c foo). */
embedded_arg = extra_opt && takes_arg;
if (takes_arg)
{
if (embedded_arg)
{
optarg = argv[0] + 2;
optind++;
}
else
{
optarg = argv[1];
optind += 2;
}
return optarg ? opt[0] : '?';
}
if (embedded_arg)
{
/* If we see additional characters,
* and they don't take arguments, this
* means we have multiple flags in one. */
memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1);
return opt[0];
}
optind++;
return opt[0];
}
static int parse_long(const struct option *longopts, char * const *argv)
{
size_t indice;
const struct option *opt = NULL;
for (indice = 0; longopts[indice].name; indice++)
{
if (!strcmp(longopts[indice].name, &argv[0][2]))
{
opt = &longopts[indice];
break;
}
}
if (!opt)
return '?';
/* getopt_long has an "optional" arg, but we don't bother with that. */
if (opt->has_arg && !argv[1])
return '?';
if (opt->has_arg)
{
optarg = argv[1];
optind += 2;
}
else
optind++;
if (opt->flag)
{
*opt->flag = opt->val;
return 0;
}
return opt->val;
}
static void shuffle_block(char **begin, char **last, char **end)
{
ptrdiff_t len = last - begin;
const char **tmp = (const char**)calloc(len, sizeof(const char*));
retro_assert(tmp);
memcpy((void*)tmp, begin, len * sizeof(const char*));
memmove(begin, last, (end - last) * sizeof(const char*));
memcpy(end - len, tmp, len * sizeof(const char*));
free((void*)tmp);
}
int getopt_long(int argc, char *argv[],
const char *optstring, const struct option *longopts, int *longindex)
{
int short_index, long_index;
(void)longindex;
if (optind == 0)
optind = 1;
if (argc < 2)
return -1;
short_index = find_short_index(&argv[optind]);
long_index = find_long_index(&argv[optind]);
/* We're done here. */
if (short_index == -1 && long_index == -1)
return -1;
/* Reorder argv so that non-options come last.
* Non-POSIXy, but that's what getopt does by default. */
if ((short_index > 0) && ((short_index < long_index) || (long_index == -1)))
{
shuffle_block(&argv[optind], &argv[optind + short_index], &argv[argc]);
short_index = 0;
}
else if ((long_index > 0) && ((long_index < short_index)
|| (short_index == -1)))
{
shuffle_block(&argv[optind], &argv[optind + long_index], &argv[argc]);
long_index = 0;
}
retro_assert(short_index == 0 || long_index == 0);
if (short_index == 0)
return parse_short(optstring, &argv[optind]);
if (long_index == 0)
return parse_long(longopts, &argv[optind]);
return '?';
}

619
compat/compat_ifaddrs.c Normal file
View 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);
}
}

View File

@ -0,0 +1,104 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_posix_string.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <ctype.h>
#include <compat/posix_string.h>
#ifdef _WIN32
#undef strcasecmp
#undef strdup
#undef isblank
#undef strtok_r
#include <ctype.h>
#include <stdlib.h>
#include <stddef.h>
#include <compat/strl.h>
#include <string.h>
int retro_strcasecmp__(const char *a, const char *b)
{
while (*a && *b)
{
int a_ = tolower(*a);
int b_ = tolower(*b);
if (a_ != b_)
return a_ - b_;
a++;
b++;
}
return tolower(*a) - tolower(*b);
}
char *retro_strdup__(const char *orig)
{
size_t len = strlen(orig) + 1;
char *ret = (char*)malloc(len);
if (!ret)
return NULL;
strlcpy(ret, orig, len);
return ret;
}
int retro_isblank__(int c)
{
return (c == ' ') || (c == '\t');
}
char *retro_strtok_r__(char *str, const char *delim, char **saveptr)
{
char *first = NULL;
if (!saveptr || !delim)
return NULL;
if (str)
*saveptr = str;
do
{
char *ptr = NULL;
first = *saveptr;
while (*first && strchr(delim, *first))
*first++ = '\0';
if (*first == '\0')
return NULL;
ptr = first + 1;
while (*ptr && !strchr(delim, *ptr))
ptr++;
*saveptr = ptr + (*ptr ? 1 : 0);
*ptr = '\0';
} while (strlen(first) == 0);
return first;
}
#endif

85
compat/compat_snprintf.c Normal file
View File

@ -0,0 +1,85 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_snprintf.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */
#ifdef _MSC_VER
#include <retro_common.h>
#if _MSC_VER >= 1800
#include <stdio.h> /* added for _vsnprintf_s and _vscprintf on VS2015 and VS2017 */
#endif
#include <stdarg.h>
#if _MSC_VER < 1800
#define va_copy(dst, src) ((dst) = (src))
#endif
#if _MSC_VER < 1300
#define _vscprintf c89_vscprintf_retro__
static int c89_vscprintf_retro__(const char *format, va_list pargs)
{
int retval;
va_list argcopy;
va_copy(argcopy, pargs);
retval = vsnprintf(NULL, 0, format, argcopy);
va_end(argcopy);
return retval;
}
#endif
/* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */
int c99_vsnprintf_retro__(char *outBuf, size_t size, const char *format, va_list ap)
{
int count = -1;
if (size != 0)
#if (_MSC_VER <= 1310)
count = _vsnprintf(outBuf, size - 1, format, ap);
#else
count = _vsnprintf_s(outBuf, size, size - 1, format, ap);
#endif
if (count == -1)
count = _vscprintf(format, ap);
if (count == size)
{
/* there was no room for a NULL, so truncate the last character */
outBuf[size - 1] = '\0';
}
return count;
}
int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...)
{
int count;
va_list ap;
va_start(ap, format);
count = c99_vsnprintf_retro__(outBuf, size, format, ap);
va_end(ap);
return count;
}
#endif

View File

@ -0,0 +1,58 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_strcasestr.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <ctype.h>
#include <compat/strcasestr.h>
/* Pretty much strncasecmp. */
static int casencmp(const char *a, const char *b, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
{
int a_lower = tolower(a[i]);
int b_lower = tolower(b[i]);
if (a_lower != b_lower)
return a_lower - b_lower;
}
return 0;
}
char *strcasestr_retro__(const char *haystack, const char *needle)
{
size_t i, search_off;
size_t hay_len = strlen(haystack);
size_t needle_len = strlen(needle);
if (needle_len > hay_len)
return NULL;
search_off = hay_len - needle_len;
for (i = 0; i <= search_off; i++)
if (!casencmp(haystack + i, needle, needle_len))
return (char*)haystack + i;
return NULL;
}

69
compat/compat_strl.c Normal file
View File

@ -0,0 +1,69 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_strl.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <ctype.h>
#include <compat/strl.h>
/* Implementation of strlcpy()/strlcat() based on OpenBSD. */
#ifndef __MACH__
size_t strlcpy(char *dest, const char *source, size_t size)
{
size_t src_size = 0;
size_t n = size;
if (n)
while (--n && (*dest++ = *source++)) src_size++;
if (!n)
{
if (size) *dest = '\0';
while (*source++) src_size++;
}
return src_size;
}
size_t strlcat(char *dest, const char *source, size_t size)
{
size_t len = strlen(dest);
dest += len;
if (len > size)
size = 0;
else
size -= len;
return len + strlcpy(dest, source, size);
}
#endif
char *strldup(const char *s, size_t n)
{
char *dst = (char*)malloc(sizeof(char) * (n + 1));
strlcpy(dst, s, n);
return dst;
}

44
compat/compat_vscprintf.c Normal file
View File

@ -0,0 +1,44 @@
/* Copyright (C) 2010-2017 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_snprintf.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */
#ifdef _MSC_VER
#include <retro_common.h>
#include <stdio.h>
#include <stdarg.h>
#if defined(_MSC_VER) && _MSC_VER < 1800
#define va_copy(dst, src) ((dst) = (src))
#endif
int c89_vscprintf_retro__(const char *format, va_list pargs)
{
int retval;
va_list argcopy;
va_copy(argcopy, pargs);
retval = vsnprintf(NULL, 0, format, argcopy);
va_end(argcopy);
return retval;
}
#endif

60
compat/fopen_utf8.c Normal file
View File

@ -0,0 +1,60 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fopen_utf8.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 <compat/fopen_utf8.h>
#include <encodings/utf.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
#ifdef _WIN32
#undef fopen
void *fopen_utf8(const char * filename, const char * mode)
{
#if defined(_XBOX)
return fopen(filename, mode);
#elif defined(LEGACY_WIN32)
FILE *ret = NULL;
char * filename_local = utf8_to_local_string_alloc(filename);
if (!filename_local)
return NULL;
ret = fopen(filename_local, mode);
if (filename_local)
free(filename_local);
return ret;
#else
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
wchar_t * mode_w = utf8_to_utf16_string_alloc(mode);
FILE* ret = _wfopen(filename_w, mode_w);
free(filename_w);
free(mode_w);
return ret;
#endif
}
#endif

10
crt/include/string.h Normal file
View 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
crt/string.c Normal file
View 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;
}

167
dynamic/dylib.c Normal file
View File

@ -0,0 +1,167 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (dylib.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <stdio.h>
#include <dynamic/dylib.h>
#include <encodings/utf.h>
#ifdef NEED_DYNAMIC
#ifdef _WIN32
#include <compat/posix_string.h>
#include <windows.h>
#else
#include <dlfcn.h>
#endif
/* Assume W-functions do not work below Win2K and Xbox platforms */
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
#ifdef _WIN32
static char last_dyn_error[512];
static void set_dl_error(void)
{
DWORD err = GetLastError();
if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
err,
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
last_dyn_error,
sizeof(last_dyn_error) - 1,
NULL) == 0)
snprintf(last_dyn_error, sizeof(last_dyn_error) - 1,
"unknown error %lu", err);
}
#endif
/**
* dylib_load:
* @path : Path to libretro core library.
*
* Platform independent dylib loading.
*
* Returns: library handle on success, otherwise NULL.
**/
dylib_t dylib_load(const char *path)
{
#ifdef _WIN32
int prevmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
#ifdef LEGACY_WIN32
dylib_t lib = LoadLibrary(path);
#else
wchar_t *pathW = utf8_to_utf16_string_alloc(path);
dylib_t lib = LoadLibraryW(pathW);
free(pathW);
#endif
SetErrorMode(prevmode);
if (!lib)
{
set_dl_error();
return NULL;
}
last_dyn_error[0] = 0;
#else
dylib_t lib = dlopen(path, RTLD_LAZY);
#endif
return lib;
}
char *dylib_error(void)
{
#ifdef _WIN32
if (last_dyn_error[0])
return last_dyn_error;
return NULL;
#else
return (char*)dlerror();
#endif
}
function_t dylib_proc(dylib_t lib, const char *proc)
{
function_t sym;
#ifdef _WIN32
sym = (function_t)GetProcAddress(lib ?
(HMODULE)lib : GetModuleHandle(NULL), proc);
if (!sym)
{
set_dl_error();
return NULL;
}
last_dyn_error[0] = 0;
#else
void *ptr_sym = NULL;
if (lib)
ptr_sym = dlsym(lib, proc);
else
{
void *handle = dlopen(NULL, RTLD_LAZY);
if (handle)
{
ptr_sym = dlsym(handle, proc);
dlclose(handle);
}
}
/* Dirty hack to workaround the non-legality of
* (void*) -> fn-pointer casts. */
memcpy(&sym, &ptr_sym, sizeof(void*));
#endif
return sym;
}
/**
* dylib_close:
* @lib : Library handle.
*
* Frees library handle.
**/
void dylib_close(dylib_t lib)
{
#ifdef _WIN32
if (!FreeLibrary((HMODULE)lib))
set_dl_error();
last_dyn_error[0] = 0;
#else
#ifndef NO_DLCLOSE
dlclose(lib);
#endif
#endif
}
#endif

View File

@ -0,0 +1,90 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (encoding_crc32.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <stddef.h>
#include <encodings/crc32.h>
static const uint32_t crc32_table[256] = {
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len)
{
crc = crc ^ 0xffffffff;
while (len--)
crc = crc32_table[(crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
return crc ^ 0xffffffff;
}

516
encodings/encoding_utf.c Normal file
View File

@ -0,0 +1,516 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (encoding_utf.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <boolean.h>
#include <compat/strl.h>
#include <retro_inline.h>
#include <encodings/utf.h>
#if defined(_WIN32) && !defined(_XBOX)
#include <windows.h>
#elif defined(_XBOX)
#include <xtl.h>
#endif
static unsigned leading_ones(uint8_t c)
{
unsigned ones = 0;
while (c & 0x80)
{
ones++;
c <<= 1;
}
return ones;
}
/* Simple implementation. Assumes the sequence is
* properly synchronized and terminated. */
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
const char *in, size_t in_size)
{
unsigned i;
size_t ret = 0;
while (in_size && out_chars)
{
unsigned extra, shift;
uint32_t c;
uint8_t first = *in++;
unsigned ones = leading_ones(first);
if (ones > 6 || ones == 1) /* Invalid or desync. */
break;
extra = ones ? ones - 1 : ones;
if (1 + extra > in_size) /* Overflow. */
break;
shift = (extra - 1) * 6;
c = (first & ((1 << (7 - ones)) - 1)) << (6 * extra);
for (i = 0; i < extra; i++, in++, shift -= 6)
c |= (*in & 0x3f) << shift;
*out++ = c;
in_size -= 1 + extra;
out_chars--;
ret++;
}
return ret;
}
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
const uint16_t *in, size_t in_size)
{
static uint8_t kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
size_t out_pos = 0;
size_t in_pos = 0;
for (;;)
{
unsigned numAdds;
uint32_t value;
if (in_pos == in_size)
{
*out_chars = out_pos;
return true;
}
value = in[in_pos++];
if (value < 0x80)
{
if (out)
out[out_pos] = (char)value;
out_pos++;
continue;
}
if (value >= 0xD800 && value < 0xE000)
{
uint32_t c2;
if (value >= 0xDC00 || in_pos == in_size)
break;
c2 = in[in_pos++];
if (c2 < 0xDC00 || c2 >= 0xE000)
break;
value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
}
for (numAdds = 1; numAdds < 5; numAdds++)
if (value < (((uint32_t)1) << (numAdds * 5 + 6)))
break;
if (out)
out[out_pos] = (char)(kUtf8Limits[numAdds - 1]
+ (value >> (6 * numAdds)));
out_pos++;
do
{
numAdds--;
if (out)
out[out_pos] = (char)(0x80
+ ((value >> (6 * numAdds)) & 0x3F));
out_pos++;
}while (numAdds != 0);
}
*out_chars = out_pos;
return false;
}
/* Acts mostly like strlcpy.
*
* Copies the given number of UTF-8 characters,
* but at most d_len bytes.
*
* Always NULL terminates.
* Does not copy half a character.
*
* Returns number of bytes. 's' is assumed valid UTF-8.
* Use only if 'chars' is considerably less than 'd_len'. */
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
{
const uint8_t *sb = (const uint8_t*)s;
const uint8_t *sb_org = sb;
if (!s)
return 0;
while (*sb && chars-- > 0)
{
sb++;
while ((*sb & 0xC0) == 0x80) sb++;
}
if ((size_t)(sb - sb_org) > d_len-1 /* NUL */)
{
sb = sb_org + d_len-1;
while ((*sb & 0xC0) == 0x80) sb--;
}
memcpy(d, sb_org, sb-sb_org);
d[sb-sb_org] = '\0';
return sb-sb_org;
}
const char *utf8skip(const char *str, size_t chars)
{
const uint8_t *strb = (const uint8_t*)str;
if (!chars)
return str;
do
{
strb++;
while ((*strb & 0xC0)==0x80) strb++;
chars--;
} while(chars);
return (const char*)strb;
}
size_t utf8len(const char *string)
{
size_t ret = 0;
if (!string)
return 0;
while (*string)
{
if ((*string & 0xC0) != 0x80)
ret++;
string++;
}
return ret;
}
static uint8_t utf8_walkbyte(const char **string)
{
return *((*string)++);
}
/* Does not validate the input, returns garbage if it's not UTF-8. */
uint32_t utf8_walk(const char **string)
{
uint8_t first = utf8_walkbyte(string);
uint32_t ret = 0;
if (first < 128)
return first;
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xE0)
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xF0)
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xF0)
return ret | (first & 7) << 18;
if (first >= 0xE0)
return ret | (first & 15) << 12;
return ret | (first & 31) << 6;
}
static bool utf16_to_char(uint8_t **utf_data,
size_t *dest_len, const uint16_t *in)
{
unsigned len = 0;
while (in[len] != '\0')
len++;
utf16_conv_utf8(NULL, dest_len, in, len);
*dest_len += 1;
*utf_data = (uint8_t*)malloc(*dest_len);
if (*utf_data == 0)
return false;
return utf16_conv_utf8(*utf_data, dest_len, in, len);
}
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
{
size_t dest_len = 0;
uint8_t *utf16_data = NULL;
bool ret = utf16_to_char(&utf16_data, &dest_len, in);
if (ret)
{
utf16_data[dest_len] = 0;
strlcpy(s, (const char*)utf16_data, len);
}
free(utf16_data);
utf16_data = NULL;
return ret;
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
static char* mb_to_mb_string_alloc(const char *str,
enum CodePage cp_in, enum CodePage cp_out)
{
char *path_buf = NULL;
wchar_t *path_buf_wide = NULL;
int path_buf_len = 0;
int path_buf_wide_len = 0;
if (!str || !*str)
return NULL;
(void)path_buf;
(void)path_buf_wide;
(void)path_buf_len;
(void)path_buf_wide_len;
#if !defined(_WIN32) || defined(_XBOX)
/* assume string needs no modification if not on Windows */
return strdup(str);
#else
#ifdef UNICODE
/* TODO/FIXME: Not implemented. */
return strdup(str);
#else
/* Windows 95 will return 0 from these functions with a UTF8 codepage set without MSLU. From an unknown MSDN version (others omit this info):
* - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-8. When this is set, dwFlags must be zero.
* - Windows 95: Under the Microsoft Layer for Unicode, MultiByteToWideChar also supports CP_UTF7 and CP_UTF8.
*/
path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0);
if (path_buf_wide_len)
{
path_buf_wide = (wchar_t*)
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t));
if (path_buf_wide)
{
MultiByteToWideChar(cp_in, 0,
str, -1, path_buf_wide, path_buf_wide_len);
if (*path_buf_wide)
{
path_buf_len = WideCharToMultiByte(cp_out, 0,
path_buf_wide, -1, NULL, 0, NULL, NULL);
if (path_buf_len)
{
path_buf = (char*)
calloc(path_buf_len + sizeof(char), sizeof(char));
if (path_buf)
{
WideCharToMultiByte(cp_out, 0,
path_buf_wide, -1, path_buf,
path_buf_len, NULL, NULL);
free(path_buf_wide);
if (*path_buf)
return path_buf;
free(path_buf);
return NULL;
}
}
else
{
free(path_buf_wide);
return strdup(str);
}
}
}
}
else
return strdup(str);
if (path_buf_wide)
free(path_buf_wide);
return NULL;
#endif
#endif
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
char* utf8_to_local_string_alloc(const char *str)
{
return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
char* local_to_utf8_string_alloc(const char *str)
{
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
wchar_t* utf8_to_utf16_string_alloc(const char *str)
{
#ifdef _WIN32
int len = 0;
int out_len = 0;
#else
size_t len = 0;
size_t out_len = 0;
#endif
wchar_t *buf = NULL;
if (!str || !*str)
return NULL;
#ifdef _WIN32
len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
if (len)
{
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL;
out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len);
}
else
{
/* fallback to ANSI codepage instead */
len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
if (len)
{
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL;
out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len);
}
}
if (out_len < 0)
{
free(buf);
return NULL;
}
#else
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
len = mbstowcs(NULL, str, 0) + 1;
if (len)
{
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL;
out_len = mbstowcs(buf, str, len);
}
if (out_len == (size_t)-1)
{
free(buf);
return NULL;
}
#endif
return buf;
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
char* utf16_to_utf8_string_alloc(const wchar_t *str)
{
#ifdef _WIN32
int len = 0;
int out_len = 0;
#else
size_t len = 0;
size_t out_len = 0;
#endif
char *buf = NULL;
if (!str || !*str)
return NULL;
#ifdef _WIN32
len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
if (len)
{
buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL;
out_len = WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, len, NULL, NULL);
}
else
{
/* fallback to ANSI codepage instead */
len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
if (len)
{
buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL;
out_len = WideCharToMultiByte(CP_ACP, 0, str, -1, buf, len, NULL, NULL);
}
}
if (out_len < 0)
{
free(buf);
return NULL;
}
#else
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
len = wcstombs(NULL, str, 0) + 1;
if (len)
{
buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL;
out_len = wcstombs(buf, str, len);
}
if (out_len == (size_t)-1)
{
free(buf);
return NULL;
}
#endif
return buf;
}

813
features/features_cpu.c Normal file
View File

@ -0,0 +1,813 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (features_cpu.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <compat/strl.h>
#include <streams/file_stream.h>
#include <libretro.h>
#include <features/features_cpu.h>
#include <retro_timers.h>
#if defined(_WIN32) && !defined(_XBOX)
#include <windows.h>
#endif
#if defined(__CELLOS_LV2__)
#ifndef _PPU_INTRINSICS_H
#include <ppu_intrinsics.h>
#endif
#elif defined(_XBOX360)
#include <PPCIntrinsics.h>
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID) || defined(__QNX__) || defined(DJGPP)
/* POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present. */
#include <time.h>
#endif
#if defined(__QNX__) && !defined(CLOCK_MONOTONIC)
#define CLOCK_MONOTONIC 2
#endif
#if defined(PSP)
#include <pspkernel.h>
#include <sys/time.h>
#include <psprtc.h>
#endif
#if defined(VITA)
#include <psp2/kernel/processmgr.h>
#include <psp2/rtc.h>
#endif
#if defined(__PSL1GHT__)
#include <sys/time.h>
#elif defined(__CELLOS_LV2__)
#include <sys/sys_time.h>
#endif
#ifdef GEKKO
#include <ogc/lwp_watchdog.h>
#endif
#ifdef WIIU
#include <wiiu/os/time.h>
#endif
#ifdef SWITCH
#include <libtransistor/types.h>
#include <libtransistor/svc.h>
#endif
#if defined(_3DS)
#include <3ds/svc.h>
#include <3ds/os.h>
#include <3ds/services/cfgu.h>
#endif
/* iOS/OSX specific. Lacks clock_gettime(), so implement it. */
#ifdef __MACH__
#include <sys/time.h>
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 0
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
/* this function is part of iOS 10 now */
static int ra_clock_gettime(int clk_ik, struct timespec *t)
{
struct timeval now;
int rv = gettimeofday(&now, NULL);
if (rv)
return rv;
t->tv_sec = now.tv_sec;
t->tv_nsec = now.tv_usec * 1000;
return 0;
}
#endif
#if defined(__MACH__) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000
#else
#define ra_clock_gettime clock_gettime
#endif
#ifdef EMSCRIPTEN
#include <emscripten.h>
#endif
#if defined(BSD) || defined(__APPLE__)
#include <sys/sysctl.h>
#endif
#include <string.h>
/**
* cpu_features_get_perf_counter:
*
* Gets performance counter.
*
* Returns: performance counter.
**/
retro_perf_tick_t cpu_features_get_perf_counter(void)
{
retro_perf_tick_t time_ticks = 0;
#if defined(_WIN32)
long tv_sec, tv_usec;
#if defined(_MSC_VER) && _MSC_VER <= 1200
static const unsigned __int64 epoch = 11644473600000000;
#else
static const unsigned __int64 epoch = 11644473600000000ULL;
#endif
FILETIME file_time;
SYSTEMTIME system_time;
ULARGE_INTEGER ularge;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
ularge.LowPart = file_time.dwLowDateTime;
ularge.HighPart = file_time.dwHighDateTime;
tv_sec = (long)((ularge.QuadPart - epoch) / 10000000L);
tv_usec = (long)(system_time.wMilliseconds * 1000);
time_ticks = (1000000 * tv_sec + tv_usec);
#elif defined(__linux__) || defined(__QNX__) || defined(__MACH__)
struct timespec tv = {0};
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) == 0)
time_ticks = (retro_perf_tick_t)tv.tv_sec * 1000000000 +
(retro_perf_tick_t)tv.tv_nsec;
#elif defined(__GNUC__) && defined(__i386__) || defined(__i486__) || defined(__i686__)
__asm__ volatile ("rdtsc" : "=A" (time_ticks));
#elif defined(__GNUC__) && defined(__x86_64__)
unsigned a, d;
__asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
time_ticks = (retro_perf_tick_t)a | ((retro_perf_tick_t)d << 32);
#elif defined(__ARM_ARCH_6__)
__asm__ volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"(time_ticks) );
#elif defined(__CELLOS_LV2__) || defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__)
time_ticks = __mftb();
#elif defined(GEKKO)
time_ticks = gettime();
#elif defined(PSP)
sceRtcGetCurrentTick((uint64_t*)&time_ticks);
#elif defined(VITA)
sceRtcGetCurrentTick((SceRtcTick*)&time_ticks);
#elif defined(_3DS)
time_ticks = svcGetSystemTick();
#elif defined(WIIU)
time_ticks = OSGetSystemTime();
#elif defined(__mips__)
struct timeval tv;
gettimeofday(&tv,NULL);
time_ticks = (1000000 * tv.tv_sec + tv.tv_usec);
#endif
return time_ticks;
}
/**
* cpu_features_get_time_usec:
*
* Gets time in microseconds.
*
* Returns: time in microseconds.
**/
retro_time_t cpu_features_get_time_usec(void)
{
#if defined(_WIN32)
static LARGE_INTEGER freq;
LARGE_INTEGER count;
/* Frequency is guaranteed to not change. */
if (!freq.QuadPart && !QueryPerformanceFrequency(&freq))
return 0;
if (!QueryPerformanceCounter(&count))
return 0;
return count.QuadPart * 1000000 / freq.QuadPart;
#elif defined(__CELLOS_LV2__)
return sys_time_get_system_time();
#elif defined(GEKKO)
return ticks_to_microsecs(gettime());
#elif defined(SWITCH)
return (svcGetSystemTick() * 10) / 192;
#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__) || (defined(_M_X64) && _MSC_VER > 1310) || (defined(_M_IX86) && _MSC_VER > 1310)
#define CPU_X86
#endif
#if defined(_MSC_VER) && !defined(_XBOX)
#if (_MSC_VER > 1310)
#include <intrin.h>
#endif
#endif
#if defined(CPU_X86) && !defined(__MACH__)
void x86_cpuid(int func, int flags[4])
{
/* On Android, we compile RetroArch with PIC, and we
* are not allowed to clobber the ebx register. */
#ifdef __x86_64__
#define REG_b "rbx"
#define REG_S "rsi"
#else
#define REG_b "ebx"
#define REG_S "esi"
#endif
#if defined(__GNUC__)
__asm__ volatile (
"mov %%" REG_b ", %%" REG_S "\n"
"cpuid\n"
"xchg %%" REG_b ", %%" REG_S "\n"
: "=a"(flags[0]), "=S"(flags[1]), "=c"(flags[2]), "=d"(flags[3])
: "a"(func));
#elif defined(_MSC_VER)
__cpuid(flags, func);
#else
printf("Unknown compiler. Cannot check CPUID with inline assembly.\n");
memset(flags, 0, 4 * sizeof(int));
#endif
}
/* Only runs on i686 and above. Needs to be conditionally run. */
static uint64_t xgetbv_x86(uint32_t idx)
{
#if defined(__GNUC__)
uint32_t eax, edx;
__asm__ volatile (
/* Older GCC versions (Apple's GCC for example) do
* not understand xgetbv instruction.
* Stamp out the machine code directly.
*/
".byte 0x0f, 0x01, 0xd0\n"
: "=a"(eax), "=d"(edx) : "c"(idx));
return ((uint64_t)edx << 32) | eax;
#elif _MSC_FULL_VER >= 160040219
/* Intrinsic only works on 2010 SP1 and above. */
return _xgetbv(idx);
#else
printf("Unknown compiler. Cannot check xgetbv bits.\n");
return 0;
#endif
}
#endif
#if defined(__ARM_NEON__)
static void arm_enable_runfast_mode(void)
{
/* RunFast mode. Enables flush-to-zero and some
* floating point optimizations. */
static const unsigned x = 0x04086060;
static const unsigned y = 0x03000000;
int r;
__asm__ volatile(
"fmrx %0, fpscr \n\t" /* r0 = FPSCR */
"and %0, %0, %1 \n\t" /* r0 = r0 & 0x04086060 */
"orr %0, %0, %2 \n\t" /* r0 = r0 | 0x03000000 */
"fmxr fpscr, %0 \n\t" /* FPSCR = r0 */
: "=r"(r)
: "r"(x), "r"(y)
);
}
#endif
#if defined(__linux__) && !defined(CPU_X86)
static unsigned char check_arm_cpu_feature(const char* feature)
{
char line[1024];
unsigned char status = 0;
RFILE *fp = filestream_open("/proc/cpuinfo",
RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!fp)
return 0;
while (filestream_gets(fp, line, sizeof(line)) != NULL)
{
if (strncmp(line, "Features\t: ", 11))
continue;
if (strstr(line + 11, feature) != NULL)
status = 1;
break;
}
filestream_close(fp);
return status;
}
#if !defined(_SC_NPROCESSORS_ONLN)
/* Parse an decimal integer starting from 'input', but not going further
* than 'limit'. Return the value into '*result'.
*
* NOTE: Does not skip over leading spaces, or deal with sign characters.
* NOTE: Ignores overflows.
*
* The function returns NULL in case of error (bad format), or the new
* position after the decimal number in case of success (which will always
* be <= 'limit').
*/
static const char *parse_decimal(const char* input,
const char* limit, int* result)
{
const char* p = input;
int val = 0;
while (p < limit)
{
int d = (*p - '0');
if ((unsigned)d >= 10U)
break;
val = val*10 + d;
p++;
}
if (p == input)
return NULL;
*result = val;
return p;
}
/* Parse a textual list of cpus and store the result inside a CpuList object.
* Input format is the following:
* - comma-separated list of items (no spaces)
* - each item is either a single decimal number (cpu index), or a range made
* of two numbers separated by a single dash (-). Ranges are inclusive.
*
* Examples: 0
* 2,4-127,128-143
* 0-1
*/
static void cpulist_parse(CpuList* list, char **buf, ssize_t length)
{
const char* p = (const char*)buf;
const char* end = p + length;
/* NOTE: the input line coming from sysfs typically contains a
* trailing newline, so take care of it in the code below
*/
while (p < end && *p != '\n')
{
int val, start_value, end_value;
/* Find the end of current item, and put it into 'q' */
const char *q = (const char*)memchr(p, ',', end-p);
if (!q)
q = end;
/* Get first value */
p = parse_decimal(p, q, &start_value);
if (p == NULL)
return;
end_value = start_value;
/* If we're not at the end of the item, expect a dash and
* and integer; extract end value.
*/
if (p < q && *p == '-')
{
p = parse_decimal(p+1, q, &end_value);
if (p == NULL)
return;
}
/* Set bits CPU list bits */
for (val = start_value; val <= end_value; val++)
{
if ((unsigned)val < 32)
list->mask |= (uint32_t)(1U << val);
}
/* Jump to next item */
p = q;
if (p < end)
p++;
}
}
/* Read a CPU list from one sysfs file */
static void cpulist_read_from(CpuList* list, const char* filename)
{
ssize_t length;
char *buf = NULL;
list->mask = 0;
if (filestream_read_file(filename, (void**)&buf, &length) != 1)
return;
cpulist_parse(list, &buf, length);
if (buf)
free(buf);
buf = NULL;
}
#endif
#endif
/**
* cpu_features_get_core_amount:
*
* Gets the amount of available CPU cores.
*
* Returns: amount of CPU cores available.
**/
unsigned cpu_features_get_core_amount(void)
{
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
/* Win32 */
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#elif defined(GEKKO)
return 1;
#elif defined(PSP)
return 1;
#elif defined(VITA)
return 4;
#elif defined(_3DS)
u8 device_model = 0xFF;
CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/
switch (device_model)
{
case 0:
case 1:
case 3:
/*Old 3/2DS*/
return 2;
case 2:
case 4:
case 5:
/*New 3/2DS*/
return 4;
default:
/*Unknown Device Or Check Failed*/
break;
}
return 1;
#elif defined(WIIU)
return 3;
#elif defined(_SC_NPROCESSORS_ONLN)
/* Linux, most UNIX-likes. */
long ret = sysconf(_SC_NPROCESSORS_ONLN);
if (ret <= 0)
return (unsigned)1;
return (unsigned)ret;
#elif defined(BSD) || defined(__APPLE__)
/* BSD */
/* Copypasta from stackoverflow, dunno if it works. */
int num_cpu = 0;
int mib[4];
size_t len = sizeof(num_cpu);
mib[0] = CTL_HW;
mib[1] = HW_AVAILCPU;
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
if (num_cpu < 1)
{
mib[1] = HW_NCPU;
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
if (num_cpu < 1)
num_cpu = 1;
}
return num_cpu;
#elif defined(__linux__)
CpuList cpus_present[1];
CpuList cpus_possible[1];
int amount = 0;
cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present");
cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible");
/* Compute the intersection of both sets to get the actual number of
* CPU cores that can be used on this device by the kernel.
*/
cpus_present->mask &= cpus_possible->mask;
amount = __builtin_popcount(cpus_present->mask);
if (amount == 0)
return 1;
return amount;
#elif defined(_XBOX360)
return 3;
#else
/* No idea, assume single core. */
return 1;
#endif
}
/* According to http://en.wikipedia.org/wiki/CPUID */
#define VENDOR_INTEL_b 0x756e6547
#define VENDOR_INTEL_c 0x6c65746e
#define VENDOR_INTEL_d 0x49656e69
/**
* cpu_features_get:
*
* Gets CPU features..
*
* Returns: bitmask of all CPU features available.
**/
uint64_t cpu_features_get(void)
{
int flags[4];
int vendor_shuffle[3];
char vendor[13];
size_t len = 0;
uint64_t cpu_flags = 0;
uint64_t cpu = 0;
unsigned max_flag = 0;
#if defined(CPU_X86) && !defined(__MACH__)
int vendor_is_intel = 0;
const int avx_flags = (1 << 27) | (1 << 28);
#endif
char buf[sizeof(" MMX MMXEXT SSE SSE2 SSE3 SSSE3 SS4 SSE4.2 AES AVX AVX2 NEON VMX VMX128 VFPU PS")];
memset(buf, 0, sizeof(buf));
(void)len;
(void)cpu_flags;
(void)flags;
(void)max_flag;
(void)vendor;
(void)vendor_shuffle;
#if defined(__MACH__)
len = sizeof(size_t);
if (sysctlbyname("hw.optional.mmx", NULL, &len, NULL, 0) == 0)
{
cpu |= RETRO_SIMD_MMX;
cpu |= RETRO_SIMD_MMXEXT;
}
len = sizeof(size_t);
if (sysctlbyname("hw.optional.floatingpoint", NULL, &len, NULL, 0) == 0)
{
cpu |= RETRO_SIMD_CMOV;
}
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse2", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE2;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse3", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE3;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.supplementalsse3", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSSE3;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse4_1", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE4;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse4_2", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE42;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.aes", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AES;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.avx1_0", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AVX;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.avx2_0", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AVX2;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.altivec", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_VMX;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.neon", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_NEON;
#elif defined(_XBOX1)
cpu |= RETRO_SIMD_MMX;
cpu |= RETRO_SIMD_SSE;
cpu |= RETRO_SIMD_MMXEXT;
#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
file/archive_file.c Normal file
View File

@ -0,0 +1,897 @@
/* Copyright (C) 2010-2018 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)
{
int64_t ret = -1;
bool read_from_file = false;
file_archive_file_data_t *data = (file_archive_file_data_t*)
calloc(1, sizeof(*data));
if (!data)
return NULL;
read_from_file = filestream_read_file(path, &data->data, &ret);
/* Failed to open archive? */
if (!read_from_file || ret < 0)
goto error;
data->size = ret;
return data;
error:
file_archive_free(data);
return NULL;
}
#endif
static int file_archive_get_file_list_cb(
const char *path,
const char *valid_exts,
const uint8_t *cdata,
unsigned cmode,
uint32_t csize,
uint32_t size,
uint32_t checksum,
struct archive_extract_userdata *userdata)
{
union string_list_elem_attr attr;
int ret = 0;
struct string_list *ext_list = NULL;
size_t path_len = strlen(path);
(void)cdata;
(void)cmode;
(void)csize;
(void)size;
(void)checksum;
attr.i = 0;
if (!path_len)
return 0;
if (valid_exts)
ext_list = string_split(valid_exts, "|");
if (ext_list)
{
const char *file_ext = NULL;
/* Checks if this entry is a directory or a file. */
char last_char = path[path_len-1];
/* Skip if directory. */
if (last_char == '/' || last_char == '\\' )
goto error;
file_ext = path_get_extension(path);
if (!file_ext)
goto error;
if (!string_list_find_elem_prefix(ext_list, ".", file_ext))
{
/* keep iterating */
ret = -1;
goto error;
}
attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE;
string_list_free(ext_list);
}
return string_list_append(userdata->list, path, attr);
error:
string_list_free(ext_list);
return ret;
}
static int file_archive_extract_cb(const char *name, const char *valid_exts,
const uint8_t *cdata,
unsigned cmode, uint32_t csize, uint32_t size,
uint32_t checksum, struct archive_extract_userdata *userdata)
{
const char *ext = path_get_extension(name);
/* Extract first file that matches our list. */
if (ext && string_list_find_elem(userdata->ext, ext))
{
char new_path[PATH_MAX_LENGTH];
char wanted_file[PATH_MAX_LENGTH];
const char *delim = NULL;
new_path[0] = wanted_file[0] = '\0';
if (userdata->extraction_directory)
fill_pathname_join(new_path, userdata->extraction_directory,
path_basename(name), sizeof(new_path));
else
fill_pathname_resolve_relative(new_path, userdata->archive_path,
path_basename(name), sizeof(new_path));
userdata->first_extracted_file_path = strdup(new_path);
delim = path_get_archive_delim(userdata->archive_path);
if (delim)
{
strlcpy(wanted_file, delim + 1, sizeof(wanted_file));
if (!string_is_equal_noncase(userdata->extracted_file_path,
wanted_file))
return 1; /* keep searching for the right file */
}
else
strlcpy(wanted_file, userdata->archive_path, sizeof(wanted_file));
if (file_archive_perform_mode(new_path,
valid_exts, cdata, cmode, csize, size,
0, userdata))
userdata->found_file = true;
return 0;
}
return 1;
}
static int file_archive_parse_file_init(file_archive_transfer_t *state,
const char *file)
{
char path[PATH_MAX_LENGTH];
char *last = NULL;
path[0] = '\0';
strlcpy(path, file, sizeof(path));
last = (char*)path_get_archive_delim(path);
if (last)
*last = '\0';
state->backend = file_archive_get_file_backend(path);
if (!state->backend)
return -1;
state->handle = file_archive_open(path);
if (!state->handle)
return -1;
state->archive_size = (int32_t)file_archive_size(state->handle);
state->data = file_archive_data(state->handle);
state->footer = 0;
state->directory = 0;
return state->backend->archive_parse_file_init(state, path);
}
/**
* file_archive_decompress_data_to_file:
* @path : filename path of archive.
* @valid_exts : Valid extensions of archive to be parsed.
* If NULL, allow all.
* @cdata : input data.
* @csize : size of input data.
* @size : output file size
* @checksum : CRC32 checksum from input data.
*
* Decompress data to file.
*
* Returns: true (1) on success, otherwise false (0).
**/
static int file_archive_decompress_data_to_file(
file_archive_file_handle_t *handle,
int ret,
const char *path,
const char *valid_exts,
const uint8_t *cdata,
uint32_t csize,
uint32_t size,
uint32_t checksum)
{
if (!handle || ret == -1)
{
ret = 0;
goto end;
}
#if 0
handle->real_checksum = handle->backend->stream_crc_calculate(
0, handle->data, size);
if (handle->real_checksum != checksum)
{
/* File CRC difers from archive CRC. */
printf("File CRC differs from archive CRC. File: 0x%x, Archive: 0x%x.\n",
(unsigned)handle->real_checksum, (unsigned)checksum);
}
#endif
if (!filestream_write_file(path, handle->data, size))
{
ret = false;
goto end;
}
end:
if (handle)
{
if (handle->backend)
{
if (handle->backend->stream_free)
handle->backend->stream_free(handle->stream);
}
if (handle->data)
free(handle->data);
}
return ret;
}
void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state)
{
if (!state || !state->handle)
return;
state->type = ARCHIVE_TRANSFER_DEINIT;
file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL);
}
int file_archive_parse_file_iterate(
file_archive_transfer_t *state,
bool *returnerr,
const char *file,
const char *valid_exts,
file_archive_file_cb file_cb,
struct archive_extract_userdata *userdata)
{
if (!state)
return -1;
switch (state->type)
{
case ARCHIVE_TRANSFER_NONE:
break;
case ARCHIVE_TRANSFER_INIT:
if (file_archive_parse_file_init(state, file) == 0)
{
if (userdata)
{
userdata->context = state->stream;
strlcpy(userdata->archive_path, file,
sizeof(userdata->archive_path));
}
state->type = ARCHIVE_TRANSFER_ITERATE;
}
else
state->type = ARCHIVE_TRANSFER_DEINIT_ERROR;
break;
case ARCHIVE_TRANSFER_ITERATE:
if (file_archive_get_file_backend(file))
{
const struct file_archive_file_backend *backend =
file_archive_get_file_backend(file);
int ret =
backend->archive_parse_file_iterate_step(state,
valid_exts, userdata, file_cb);
if (ret != 1)
state->type = ARCHIVE_TRANSFER_DEINIT;
if (ret == -1)
state->type = ARCHIVE_TRANSFER_DEINIT_ERROR;
/* early return to prevent deinit from never firing */
return 0;
}
return -1;
case ARCHIVE_TRANSFER_DEINIT_ERROR:
*returnerr = false;
case ARCHIVE_TRANSFER_DEINIT:
if (state->handle)
{
file_archive_free(state->handle);
state->handle = NULL;
}
if (state->stream && state->backend)
{
if (state->backend->stream_free)
state->backend->stream_free(state->stream);
if (state->stream)
free(state->stream);
state->stream = NULL;
if (userdata)
userdata->context = NULL;
}
break;
}
if ( state->type == ARCHIVE_TRANSFER_DEINIT ||
state->type == ARCHIVE_TRANSFER_DEINIT_ERROR)
return -1;
return 0;
}
/**
* file_archive_walk:
* @file : filename path of archive
* @valid_exts : Valid extensions of archive to be parsed.
* If NULL, allow all.
* @file_cb : file_cb function pointer
* @userdata : userdata to pass to file_cb function pointer.
*
* Low-level file parsing. Enumerates over all files and calls
* file_cb with userdata.
*
* Returns: true (1) on success, otherwise false (0).
**/
static bool file_archive_walk(const char *file, const char *valid_exts,
file_archive_file_cb file_cb, struct archive_extract_userdata *userdata)
{
file_archive_transfer_t state;
bool returnerr = true;
state.type = ARCHIVE_TRANSFER_INIT;
state.archive_size = 0;
state.handle = NULL;
state.stream = NULL;
state.footer = NULL;
state.directory = NULL;
state.data = NULL;
state.backend = NULL;
for (;;)
{
if (file_archive_parse_file_iterate(&state, &returnerr, file,
valid_exts, file_cb, userdata) != 0)
break;
}
return returnerr;
}
int file_archive_parse_file_progress(file_archive_transfer_t *state)
{
/* FIXME: this estimate is worse than before */
ptrdiff_t delta = 0;
if (!state || state->archive_size == 0)
return 0;
delta = state->directory - state->data;
return (int)(delta * 100 / state->archive_size);
}
/**
* file_archive_extract_file:
* @archive_path : filename path to archive.
* @archive_path_size : size of archive.
* @valid_exts : valid extensions for the file.
* @extraction_directory : the directory to extract temporary
* file to.
*
* Extract file from archive. If no file inside the archive is
* specified, the first file found will be used.
*
* Returns : true (1) on success, otherwise false (0).
**/
bool file_archive_extract_file(
char *archive_path,
size_t archive_path_size,
const char *valid_exts,
const char *extraction_directory,
char *out_path, size_t len)
{
struct archive_extract_userdata userdata;
bool ret = true;
struct string_list *list = string_split(valid_exts, "|");
userdata.archive_path[0] = '\0';
userdata.first_extracted_file_path = NULL;
userdata.extracted_file_path = NULL;
userdata.extraction_directory = extraction_directory;
userdata.archive_path_size = archive_path_size;
userdata.ext = list;
userdata.list = NULL;
userdata.found_file = false;
userdata.list_only = false;
userdata.context = NULL;
userdata.archive_name[0] = '\0';
userdata.crc = 0;
userdata.dec = NULL;
userdata.decomp_state.opt_file = NULL;
userdata.decomp_state.needle = NULL;
userdata.decomp_state.size = 0;
userdata.decomp_state.found = false;
if (!list)
{
ret = false;
goto end;
}
if (!file_archive_walk(archive_path, valid_exts,
file_archive_extract_cb, &userdata))
{
/* Parsing file archive failed. */
ret = false;
goto end;
}
if (!userdata.found_file)
{
/* Didn't find any file that matched valid extensions
* for libretro implementation. */
ret = false;
goto end;
}
if (!string_is_empty(userdata.first_extracted_file_path))
strlcpy(out_path, userdata.first_extracted_file_path, len);
end:
if (userdata.first_extracted_file_path)
free(userdata.first_extracted_file_path);
if (list)
string_list_free(list);
return ret;
}
/**
* file_archive_get_file_list:
* @path : filename path of archive
*
* Returns: string listing of files from archive on success, otherwise NULL.
**/
struct string_list *file_archive_get_file_list(const char *path,
const char *valid_exts)
{
int ret;
struct archive_extract_userdata userdata;
strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path));
userdata.first_extracted_file_path = NULL;
userdata.extracted_file_path = NULL;
userdata.extraction_directory = NULL;
userdata.archive_path_size = 0;
userdata.ext = NULL;
userdata.list = string_list_new();
userdata.found_file = false;
userdata.list_only = true;
userdata.context = NULL;
userdata.archive_name[0] = '\0';
userdata.crc = 0;
userdata.dec = NULL;
userdata.decomp_state.opt_file = NULL;
userdata.decomp_state.needle = NULL;
userdata.decomp_state.size = 0;
userdata.decomp_state.found = false;
if (!userdata.list)
goto error;
ret = file_archive_walk(path, valid_exts,
file_archive_get_file_list_cb, &userdata);
if (ret <= 0)
{
if (ret != -1)
goto error;
}
return userdata.list;
error:
if (userdata.list)
string_list_free(userdata.list);
return NULL;
}
bool file_archive_perform_mode(const char *path, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
uint32_t crc32, struct archive_extract_userdata *userdata)
{
switch (cmode)
{
case ARCHIVE_MODE_UNCOMPRESSED:
if (!filestream_write_file(path, cdata, size))
goto error;
break;
case ARCHIVE_MODE_COMPRESSED:
{
int ret = 0;
file_archive_file_handle_t handle;
handle.stream = userdata->context;
handle.data = NULL;
handle.real_checksum = 0;
handle.backend = file_archive_get_file_backend(userdata->archive_path);
if (!handle.backend)
goto error;
if (!handle.backend->stream_decompress_data_to_file_init(&handle,
cdata, csize, size))
goto error;
do
{
ret = handle.backend->stream_decompress_data_to_file_iterate(
handle.stream);
}while(ret == 0);
if (!file_archive_decompress_data_to_file(&handle,
ret, path, valid_exts,
cdata, csize, size, crc32))
goto error;
}
break;
default:
goto error;
}
return true;
error:
return false;
}
/**
* file_archive_filename_split:
* @str : filename to turn into a string list
*
* Creates a new string list based on filename @path, delimited by a hash (#).
*
* Returns: new string list if successful, otherwise NULL.
*/
static struct string_list *file_archive_filename_split(const char *path)
{
union string_list_elem_attr attr;
struct string_list *list = string_list_new();
const char *delim = path_get_archive_delim(path);
attr.i = 0;
if (delim)
{
/* add archive path to list first */
if (!string_list_append_n(list, path, (unsigned)(delim - path), attr))
goto error;
/* now add the path within the archive */
delim++;
if (*delim)
{
if (!string_list_append(list, delim, attr))
goto error;
}
}
else
if (!string_list_append(list, path, attr))
goto error;
return list;
error:
string_list_free(list);
return NULL;
}
/* Generic compressed file loader.
* Extracts to buf, unless optional_filename != 0
* Then extracts to optional_filename and leaves buf alone.
*/
int file_archive_compressed_read(
const char * path, void **buf,
const char* optional_filename, int64_t *length)
{
const struct file_archive_file_backend *backend = NULL;
int ret = 0;
struct string_list *str_list = file_archive_filename_split(path);
/* Safety check.
* If optional_filename and optional_filename
* exists, we simply return 0,
* hoping that optional_filename is the
* same as requested.
*/
if (optional_filename && filestream_exists(optional_filename))
{
*length = 0;
string_list_free(str_list);
return 1;
}
/* We assure that there is something after the '#' symbol.
*
* This error condition happens for example, when
* path = /path/to/file.7z, or
* path = /path/to/file.7z#
*/
if (str_list->size <= 1)
goto error;
backend = file_archive_get_file_backend(str_list->elems[0].data);
*length = backend->compressed_file_read(str_list->elems[0].data,
str_list->elems[1].data, buf, optional_filename);
if (*length != -1)
ret = 1;
string_list_free(str_list);
return ret;
error:
/* could not extract string and substring. */
string_list_free(str_list);
*length = 0;
return 0;
}
const struct file_archive_file_backend *file_archive_get_zlib_file_backend(void)
{
#ifdef HAVE_ZLIB
return &zlib_backend;
#else
return NULL;
#endif
}
const struct file_archive_file_backend *file_archive_get_7z_file_backend(void)
{
#ifdef HAVE_7ZIP
return &sevenzip_backend;
#else
return NULL;
#endif
}
const struct file_archive_file_backend* file_archive_get_file_backend(const char *path)
{
char newpath[PATH_MAX_LENGTH];
const char *file_ext = NULL;
char *last = NULL;
newpath[0] = '\0';
strlcpy(newpath, path, sizeof(newpath));
last = (char*)path_get_archive_delim(newpath);
if (last)
*last = '\0';
file_ext = path_get_extension(newpath);
#ifdef HAVE_7ZIP
if (string_is_equal_noncase(file_ext, "7z"))
return &sevenzip_backend;
#endif
#ifdef HAVE_ZLIB
if ( string_is_equal_noncase(file_ext, "zip")
|| string_is_equal_noncase(file_ext, "apk")
)
return &zlib_backend;
#endif
return NULL;
}
/**
* file_archive_get_file_crc32:
* @path : filename path of archive
*
* Returns: CRC32 of the specified file in the archive, otherwise 0.
* If no path within the archive is specified, the first
* file found inside is used.
**/
uint32_t file_archive_get_file_crc32(const char *path)
{
file_archive_transfer_t state;
const struct file_archive_file_backend *backend = file_archive_get_file_backend(path);
struct archive_extract_userdata userdata = {{0}};
bool returnerr = false;
bool contains_compressed = false;
const char *archive_path = NULL;
if (!backend)
return 0;
contains_compressed = path_contains_compressed_file(path);
if (contains_compressed)
{
archive_path = path_get_archive_delim(path);
/* move pointer right after the delimiter to give us the path */
if (archive_path)
archive_path += 1;
}
state.type = ARCHIVE_TRANSFER_INIT;
state.archive_size = 0;
state.handle = NULL;
state.stream = NULL;
state.footer = NULL;
state.directory = NULL;
state.data = NULL;
state.backend = NULL;
/* Initialize and open archive first.
Sets next state type to ITERATE. */
file_archive_parse_file_iterate(&state,
&returnerr, path, NULL, NULL,
&userdata);
for (;;)
{
/* Now find the first file in the archive. */
if (state.type == ARCHIVE_TRANSFER_ITERATE)
file_archive_parse_file_iterate(&state,
&returnerr, path, NULL, NULL,
&userdata);
/* If no path specified within archive, stop after
* finding the first file.
*/
if (!contains_compressed)
break;
/* Stop when the right file in the archive is found. */
if (archive_path)
{
if (string_is_equal(userdata.extracted_file_path, archive_path))
break;
}
else
break;
}
file_archive_parse_file_iterate_stop(&state);
if (userdata.crc)
return userdata.crc;
return 0;
}

512
file/archive_file_7z.c Normal file
View File

@ -0,0 +1,512 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (archive_file_sevenzip.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <boolean.h>
#include <file/archive_file.h>
#include <streams/file_stream.h>
#include <retro_miscellaneous.h>
#include <encodings/utf.h>
#include <encodings/crc32.h>
#include <string/stdstring.h>
#include <lists/string_list.h>
#include <file/file_path.h>
#include <compat/strl.h>
#include <7zip/7z.h>
#include <7zip/7zCrc.h>
#include <7zip/7zFile.h>
#define SEVENZIP_MAGIC "7z\xBC\xAF\x27\x1C"
#define SEVENZIP_MAGIC_LEN 6
/* Assume W-functions do not work below Win2K and Xbox platforms */
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
struct sevenzip_context_t {
CFileInStream archiveStream;
CLookToRead lookStream;
ISzAlloc allocImp;
ISzAlloc allocTempImp;
CSzArEx db;
size_t temp_size;
uint32_t block_index;
uint32_t index;
uint32_t packIndex;
uint8_t *output;
file_archive_file_handle_t *handle;
};
static void *sevenzip_stream_alloc_impl(void *p, size_t size)
{
if (size == 0)
return 0;
return malloc(size);
}
static void sevenzip_stream_free_impl(void *p, void *address)
{
(void)p;
free(address);
}
static void *sevenzip_stream_alloc_tmp_impl(void *p, size_t size)
{
(void)p;
if (size == 0)
return 0;
return malloc(size);
}
static void* sevenzip_stream_new(void)
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)calloc(1, sizeof(struct sevenzip_context_t));
/* These are the allocation routines - currently using
* the non-standard 7zip choices. */
sevenzip_context->allocImp.Alloc = sevenzip_stream_alloc_impl;
sevenzip_context->allocImp.Free = sevenzip_stream_free_impl;
sevenzip_context->allocTempImp.Alloc = sevenzip_stream_alloc_tmp_impl;
sevenzip_context->allocTempImp.Free = sevenzip_stream_free_impl;
sevenzip_context->block_index = 0xFFFFFFFF;
sevenzip_context->output = NULL;
sevenzip_context->handle = NULL;
return sevenzip_context;
}
static void sevenzip_stream_free(void *data)
{
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)data;
if (!sevenzip_context)
return;
if (sevenzip_context->output)
{
IAlloc_Free(&sevenzip_context->allocImp, sevenzip_context->output);
sevenzip_context->output = NULL;
sevenzip_context->handle->data = NULL;
}
SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp);
File_Close(&sevenzip_context->archiveStream.file);
}
/* Extract the relative path (needle) from a 7z archive
* (path) and allocate a buf for it to write it in.
* If optional_outfile is set, extract to that instead
* and don't allocate buffer.
*/
static int sevenzip_file_read(
const char *path,
const char *needle, void **buf,
const char *optional_outfile)
{
CFileInStream archiveStream;
CLookToRead lookStream;
ISzAlloc allocImp;
ISzAlloc allocTempImp;
CSzArEx db;
uint8_t *output = 0;
long outsize = -1;
/*These are the allocation routines.
* Currently using the non-standard 7zip choices. */
allocImp.Alloc = sevenzip_stream_alloc_impl;
allocImp.Free = sevenzip_stream_free_impl;
allocTempImp.Alloc = sevenzip_stream_alloc_tmp_impl;
allocTempImp.Free = sevenzip_stream_free_impl;
#if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32)
if (!string_is_empty(path))
{
wchar_t *pathW = utf8_to_utf16_string_alloc(path);
if (pathW)
{
/* Could not open 7zip archive? */
if (InFile_OpenW(&archiveStream.file, pathW))
{
free(pathW);
return -1;
}
free(pathW);
}
}
#else
/* Could not open 7zip archive? */
if (InFile_Open(&archiveStream.file, path))
return -1;
#endif
FileInStream_CreateVTable(&archiveStream);
LookToRead_CreateVTable(&lookStream, false);
lookStream.realStream = &archiveStream.s;
LookToRead_Init(&lookStream);
CrcGenerateTable();
db.db.PackSizes = NULL;
db.db.PackCRCsDefined = NULL;
db.db.PackCRCs = NULL;
db.db.Folders = NULL;
db.db.Files = NULL;
db.db.NumPackStreams = 0;
db.db.NumFolders = 0;
db.db.NumFiles = 0;
db.startPosAfterHeader = 0;
db.dataPos = 0;
db.FolderStartPackStreamIndex = NULL;
db.PackStreamStartPositions = NULL;
db.FolderStartFileIndex = NULL;
db.FileIndexToFolderIndexMap = NULL;
db.FileNameOffsets = NULL;
db.FileNames.data = NULL;
db.FileNames.size = 0;
SzArEx_Init(&db);
if (SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp) == SZ_OK)
{
uint32_t i;
bool file_found = false;
uint16_t *temp = NULL;
size_t temp_size = 0;
uint32_t block_index = 0xFFFFFFFF;
SRes res = SZ_OK;
for (i = 0; i < db.db.NumFiles; i++)
{
size_t len;
char infile[PATH_MAX_LENGTH];
size_t offset = 0;
size_t outSizeProcessed = 0;
const CSzFileItem *f = db.db.Files + i;
/* We skip over everything which is not a directory.
* FIXME: Why continue then if f->IsDir is true?*/
if (f->IsDir)
continue;
len = SzArEx_GetFileNameUtf16(&db, i, NULL);
if (len > temp_size)
{
if (temp)
free(temp);
temp_size = len;
temp = (uint16_t *)malloc(temp_size * sizeof(temp[0]));
if (temp == 0)
{
res = SZ_ERROR_MEM;
break;
}
}
SzArEx_GetFileNameUtf16(&db, i, temp);
res = SZ_ERROR_FAIL;
infile[0] = '\0';
if (temp)
res = utf16_to_char_string(temp, infile, sizeof(infile))
? SZ_OK : SZ_ERROR_FAIL;
if (string_is_equal(infile, needle))
{
size_t output_size = 0;
/* C LZMA SDK does not support chunked extraction - see here:
* sourceforge.net/p/sevenzip/discussion/45798/thread/6fb59aaf/
* */
file_found = true;
res = SzArEx_Extract(&db, &lookStream.s, i, &block_index,
&output, &output_size, &offset, &outSizeProcessed,
&allocImp, &allocTempImp);
if (res != SZ_OK)
break; /* This goes to the error section. */
outsize = outSizeProcessed;
if (optional_outfile != NULL)
{
const void *ptr = (const void*)(output + offset);
if (!filestream_write_file(optional_outfile, ptr, outsize))
{
res = SZ_OK;
file_found = true;
outsize = -1;
}
}
else
{
/*We could either use the 7Zip allocated buffer,
* or create our own and use it.
* We would however need to realloc anyways, because RetroArch
* expects a \0 at the end, therefore we allocate new,
* copy and free the old one. */
*buf = malloc(outsize + 1);
((char*)(*buf))[outsize] = '\0';
memcpy(*buf,output + offset,outsize);
}
break;
}
}
if (temp)
free(temp);
IAlloc_Free(&allocImp, output);
if (!(file_found && res == SZ_OK))
{
/* Error handling
*
* Failed to open compressed file inside 7zip archive.
*/
outsize = -1;
}
}
SzArEx_Free(&db, &allocImp);
File_Close(&archiveStream.file);
return (int)outsize;
}
static bool sevenzip_stream_decompress_data_to_file_init(
file_archive_file_handle_t *handle,
const uint8_t *cdata, uint32_t csize, uint32_t size)
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)handle->stream;
if (!sevenzip_context)
return false;
sevenzip_context->handle = handle;
return true;
}
static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)data;
SRes res = SZ_ERROR_FAIL;
size_t output_size = 0;
size_t offset = 0;
size_t outSizeProcessed = 0;
res = SzArEx_Extract(&sevenzip_context->db,
&sevenzip_context->lookStream.s, sevenzip_context->index,
&sevenzip_context->block_index, &sevenzip_context->output,
&output_size, &offset, &outSizeProcessed,
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp);
if (res != SZ_OK)
return 0;
if (sevenzip_context->handle)
sevenzip_context->handle->data = sevenzip_context->output + offset;
return 1;
}
static int sevenzip_parse_file_init(file_archive_transfer_t *state,
const char *file)
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)sevenzip_stream_new();
if (state->archive_size < SEVENZIP_MAGIC_LEN)
goto error;
if (string_is_not_equal_fast(state->data, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN))
goto error;
state->stream = sevenzip_context;
#if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32)
if (!string_is_empty(file))
{
wchar_t *fileW = utf8_to_utf16_string_alloc(file);
if (fileW)
{
/* could not open 7zip archive? */
if (InFile_OpenW(&sevenzip_context->archiveStream.file, fileW))
{
free(fileW);
goto error;
}
free(fileW);
}
}
#else
/* could not open 7zip archive? */
if (InFile_Open(&sevenzip_context->archiveStream.file, file))
goto error;
#endif
FileInStream_CreateVTable(&sevenzip_context->archiveStream);
LookToRead_CreateVTable(&sevenzip_context->lookStream, false);
sevenzip_context->lookStream.realStream = &sevenzip_context->archiveStream.s;
LookToRead_Init(&sevenzip_context->lookStream);
CrcGenerateTable();
SzArEx_Init(&sevenzip_context->db);
if (SzArEx_Open(&sevenzip_context->db, &sevenzip_context->lookStream.s,
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp) != SZ_OK)
goto error;
return 0;
error:
if (sevenzip_context)
sevenzip_stream_free(sevenzip_context);
return -1;
}
static int sevenzip_parse_file_iterate_step_internal(
file_archive_transfer_t *state, char *filename,
const uint8_t **cdata, unsigned *cmode,
uint32_t *size, uint32_t *csize, uint32_t *checksum,
unsigned *payback, struct archive_extract_userdata *userdata)
{
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)state->stream;
const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->index;
if (sevenzip_context->index < sevenzip_context->db.db.NumFiles)
{
size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db,
sevenzip_context->index, NULL);
uint64_t compressed_size = 0;
if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams)
{
compressed_size = sevenzip_context->db.db.PackSizes[sevenzip_context->packIndex];
sevenzip_context->packIndex++;
}
if (len < PATH_MAX_LENGTH && !file->IsDir)
{
char infile[PATH_MAX_LENGTH];
SRes res = SZ_ERROR_FAIL;
uint16_t *temp = (uint16_t*)malloc(len * sizeof(uint16_t));
if (!temp)
return -1;
infile[0] = '\0';
SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index,
temp);
if (temp)
{
res = utf16_to_char_string(temp, infile, sizeof(infile))
? SZ_OK : SZ_ERROR_FAIL;
free(temp);
}
if (res != SZ_OK)
return -1;
strlcpy(filename, infile, PATH_MAX_LENGTH);
*cmode = ARCHIVE_MODE_COMPRESSED;
*checksum = file->Crc;
*size = (uint32_t)file->Size;
*csize = (uint32_t)compressed_size;
}
}
*payback = 1;
return 1;
}
static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state,
const char *valid_exts,
struct archive_extract_userdata *userdata, file_archive_file_cb file_cb)
{
char filename[PATH_MAX_LENGTH];
const uint8_t *cdata = NULL;
uint32_t checksum = 0;
uint32_t size = 0;
uint32_t csize = 0;
unsigned cmode = 0;
unsigned payload = 0;
struct sevenzip_context_t *sevenzip_context = NULL;
int ret;
filename[0] = '\0';
ret = sevenzip_parse_file_iterate_step_internal(state, filename,
&cdata, &cmode, &size, &csize,
&checksum, &payload, userdata);
if (ret != 1)
return ret;
userdata->extracted_file_path = filename;
userdata->crc = checksum;
if (file_cb && !file_cb(filename, valid_exts, cdata, cmode,
csize, size, checksum, userdata))
return 0;
sevenzip_context = (struct sevenzip_context_t*)state->stream;
sevenzip_context->index += payload;
return 1;
}
static uint32_t sevenzip_stream_crc32_calculate(uint32_t crc,
const uint8_t *data, size_t length)
{
return encoding_crc32(crc, data, length);
}
const struct file_archive_file_backend sevenzip_backend = {
sevenzip_stream_new,
sevenzip_stream_free,
sevenzip_stream_decompress_data_to_file_init,
sevenzip_stream_decompress_data_to_file_iterate,
sevenzip_stream_crc32_calculate,
sevenzip_file_read,
sevenzip_parse_file_init,
sevenzip_parse_file_iterate_step,
"7z"
};

384
file/archive_file_zlib.c Normal file
View File

@ -0,0 +1,384 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (archive_file_zlib.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <file/archive_file.h>
#include <streams/file_stream.h>
#include <streams/trans_stream.h>
#include <retro_inline.h>
#include <retro_miscellaneous.h>
#include <encodings/crc32.h>
/* Only for MAX_WBITS */
#include <compat/zlib.h>
#ifndef CENTRAL_FILE_HEADER_SIGNATURE
#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
#endif
#ifndef END_OF_CENTRAL_DIR_SIGNATURE
#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
#endif
static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
{
unsigned i;
uint32_t val = 0;
size *= 8;
for (i = 0; i < size; i += 8)
val |= (uint32_t)*data++ << i;
return val;
}
static void *zlib_stream_new(void)
{
return zlib_inflate_backend.stream_new();
}
static void zlib_stream_free(void *stream)
{
zlib_inflate_backend.stream_free(stream);
}
static bool zlib_stream_decompress_data_to_file_init(
file_archive_file_handle_t *handle,
const uint8_t *cdata, uint32_t csize, uint32_t size)
{
if (!handle)
return false;
handle->stream = zlib_inflate_backend.stream_new();
if (!handle->stream)
goto error;
if (zlib_inflate_backend.define)
zlib_inflate_backend.define(handle->stream, "window_bits", (uint32_t)-MAX_WBITS);
handle->data = (uint8_t*)malloc(size);
if (!handle->data)
goto error;
zlib_inflate_backend.set_in(handle->stream,
(const uint8_t*)cdata, csize);
zlib_inflate_backend.set_out(handle->stream,
handle->data, size);
return true;
error:
if (handle->stream)
zlib_inflate_backend.stream_free(handle->stream);
if (handle->data)
free(handle->data);
return false;
}
static int zlib_stream_decompress_data_to_file_iterate(void *stream)
{
bool zstatus;
uint32_t rd, wn;
enum trans_stream_error terror;
if (!stream)
return -1;
zstatus = zlib_inflate_backend.trans(stream, false, &rd, &wn, &terror);
if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
return -1;
if (zstatus && !terror)
return 1;
return 0;
}
static uint32_t zlib_stream_crc32_calculate(uint32_t crc,
const uint8_t *data, size_t length)
{
return encoding_crc32(crc, data, length);
}
static bool zip_file_decompressed_handle(
file_archive_file_handle_t *handle,
const uint8_t *cdata, uint32_t csize,
uint32_t size, uint32_t crc32)
{
int ret = 0;
handle->backend = &zlib_backend;
if (!handle->backend->stream_decompress_data_to_file_init(
handle, cdata, csize, size))
return false;
do
{
ret = handle->backend->stream_decompress_data_to_file_iterate(
handle->stream);
}while(ret == 0);
#if 0
handle->real_checksum = handle->backend->stream_crc_calculate(0,
handle->data, size);
if (handle->real_checksum != crc32)
goto error;
#endif
if (handle->stream)
free(handle->stream);
return true;
#if 0
error:
if (handle->stream)
free(handle->stream);
if (handle->data)
free(handle->data);
handle->stream = NULL;
handle->data = NULL;
return false;
#endif
}
/* Extract the relative path (needle) from a
* ZIP archive (path) and allocate a buffer for it to write it in.
*
* optional_outfile if not NULL will be used to extract the file to.
* buf will be 0 then.
*/
static int zip_file_decompressed(
const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode,
uint32_t csize, uint32_t size,
uint32_t crc32, struct archive_extract_userdata *userdata)
{
/* Ignore directories. */
if (name[strlen(name) - 1] == '/' || name[strlen(name) - 1] == '\\')
return 1;
if (strstr(name, userdata->decomp_state.needle))
{
bool goto_error = false;
file_archive_file_handle_t handle = {0};
userdata->decomp_state.found = true;
if (zip_file_decompressed_handle(&handle,
cdata, csize, size, crc32))
{
if (userdata->decomp_state.opt_file != 0)
{
/* Called in case core has need_fullpath enabled. */
char *buf = (char*)malloc(size);
if (buf)
{
memcpy(buf, handle.data, size);
if (!filestream_write_file(userdata->decomp_state.opt_file, buf, size))
goto_error = true;
}
free(buf);
userdata->decomp_state.size = 0;
}
else
{
/* Called in case core has need_fullpath disabled.
* Will copy decompressed content directly into
* RetroArch's ROM buffer. */
*userdata->decomp_state.buf = malloc(size);
memcpy(*userdata->decomp_state.buf, handle.data, size);
userdata->decomp_state.size = size;
}
}
if (handle.data)
free(handle.data);
if (goto_error)
return 0;
}
return 1;
}
static int zip_file_read(
const char *path,
const char *needle, void **buf,
const char *optional_outfile)
{
file_archive_transfer_t zlib = {ARCHIVE_TRANSFER_NONE, 0, NULL, NULL, NULL, NULL, NULL, NULL };
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"
};

1015
file/config_file.c Normal file

File diff suppressed because it is too large Load Diff

149
file/config_file_userdata.c Normal file
View File

@ -0,0 +1,149 @@
/* Copyright (C) 2010-2018 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);
}

1205
file/file_path.c Normal file

File diff suppressed because it is too large Load Diff

85
file/nbio/nbio_intf.c Normal file
View File

@ -0,0 +1,85 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nbio_intf.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <file/nbio.h>
extern nbio_intf_t nbio_linux;
extern nbio_intf_t nbio_mmap_unix;
extern nbio_intf_t nbio_mmap_win32;
extern nbio_intf_t nbio_stdio;
#if defined(_linux__)
static nbio_intf_t *internal_nbio = &nbio_linux;
#elif defined(HAVE_MMAP) && defined(BSD)
static nbio_intf_t *internal_nbio = &nbio_mmap_unix;
#elif defined(_WIN32) && !defined(_XBOX)
static nbio_intf_t *internal_nbio = &nbio_mmap_win32;
#else
static nbio_intf_t *internal_nbio = &nbio_stdio;
#endif
void *nbio_open(const char * filename, unsigned mode)
{
return internal_nbio->open(filename, mode);
}
void nbio_begin_read(void *data)
{
internal_nbio->begin_read(data);
}
void nbio_begin_write(void *data)
{
internal_nbio->begin_write(data);
}
bool nbio_iterate(void *data)
{
return internal_nbio->iterate(data);
}
void nbio_resize(void *data, size_t len)
{
internal_nbio->resize(data, len);
}
void *nbio_get_ptr(void *data, size_t* len)
{
return internal_nbio->get_ptr(data, len);
}
void nbio_cancel(void *data)
{
internal_nbio->cancel(data);
}
void nbio_free(void *data)
{
internal_nbio->free(data);
}

245
file/nbio/nbio_linux.c Normal file
View File

@ -0,0 +1,245 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nbio_linux.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <file/nbio.h>
#if defined(__linux__)
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <linux/aio_abi.h>
struct nbio_linux_t
{
int fd;
bool busy;
aio_context_t ctx;
struct iocb cb;
void* ptr;
size_t len;
};
/* there's also a Unix AIO thingy, but it's not in glibc
* and we don't want more dependencies */
static int io_setup(unsigned nr, aio_context_t * ctxp)
{
return syscall(__NR_io_setup, nr, ctxp);
}
static int io_destroy(aio_context_t ctx)
{
return syscall(__NR_io_destroy, ctx);
}
static int io_submit(aio_context_t ctx, long nr, struct iocb ** cbp)
{
return syscall(__NR_io_submit, ctx, nr, cbp);
}
static int io_cancel(aio_context_t ctx, struct iocb * iocb, struct io_event * result)
{
return syscall(__NR_io_cancel, ctx, iocb, result);
}
static int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event * events, struct timespec * timeout)
{
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}
static void nbio_begin_op(struct nbio_linux_t* handle, uint16_t op)
{
struct iocb * cbp = &handle->cb;
memset(&handle->cb, 0, sizeof(handle->cb));
handle->cb.aio_fildes = handle->fd;
handle->cb.aio_lio_opcode = op;
handle->cb.aio_buf = (uint64_t)(uintptr_t)handle->ptr;
handle->cb.aio_offset = 0;
handle->cb.aio_nbytes = handle->len;
if (io_submit(handle->ctx, 1, &cbp) != 1)
{
puts("ERROR - io_submit() failed");
abort();
}
handle->busy = true;
}
static void *nbio_linux_open(const char * filename, unsigned mode)
{
static const int o_flags[] = { O_RDONLY, O_RDWR|O_CREAT|O_TRUNC, O_RDWR, O_RDONLY, O_RDWR|O_CREAT|O_TRUNC };
aio_context_t ctx = 0;
struct nbio_linux_t* handle = NULL;
int fd = open(filename, o_flags[mode]|O_CLOEXEC, 0644);
if (fd < 0)
return NULL;
if (io_setup(128, &ctx) < 0)
{
close(fd);
return NULL;
}
handle = (struct nbio_linux_t*)malloc(sizeof(struct nbio_linux_t));
handle->fd = fd;
handle->ctx = ctx;
handle->len = lseek(fd, 0, SEEK_END);
handle->ptr = malloc(handle->len);
handle->busy = false;
return handle;
}
static void nbio_linux_begin_read(void *data)
{
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
if (handle)
nbio_begin_op(handle, IOCB_CMD_PREAD);
}
static void nbio_linux_begin_write(void *data)
{
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
if (handle)
nbio_begin_op(handle, IOCB_CMD_PWRITE);
}
static bool nbio_linux_iterate(void *data)
{
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
if (!handle)
return false;
if (handle->busy)
{
struct io_event ev;
if (io_getevents(handle->ctx, 0, 1, &ev, NULL) == 1)
handle->busy = false;
}
return !handle->busy;
}
static void nbio_linux_resize(void *data, size_t len)
{
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
if (!handle)
return;
if (len < handle->len)
{
/* this works perfectly fine if this check is removed, but it
* won't work on other nbio implementations */
/* therefore, it's blocked so nobody accidentally relies on it */
puts("ERROR - attempted file shrink operation, not implemented");
abort();
}
if (ftruncate(handle->fd, len) != 0)
{
puts("ERROR - couldn't resize file (ftruncate)");
abort(); /* this one returns void and I can't find any other way
for it to report failure */
}
handle->ptr = realloc(handle->ptr, len);
handle->len = len;
}
static void *nbio_linux_get_ptr(void *data, size_t* len)
{
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
if (!handle)
return NULL;
if (len)
*len = handle->len;
if (!handle->busy)
return handle->ptr;
return NULL;
}
static void nbio_linux_cancel(void *data)
{
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
if (!handle)
return;
if (handle->busy)
{
struct io_event ev;
io_cancel(handle->ctx, &handle->cb, &ev);
handle->busy = false;
}
}
static void nbio_linux_free(void *data)
{
struct nbio_linux_t* handle = (struct nbio_linux_t*)data;
if (!handle)
return;
io_destroy(handle->ctx);
close(handle->fd);
free(handle->ptr);
free(handle);
}
nbio_intf_t nbio_linux = {
nbio_linux_open,
nbio_linux_begin_read,
nbio_linux_begin_write,
nbio_linux_iterate,
nbio_linux_resize,
nbio_linux_get_ptr,
nbio_linux_cancel,
nbio_linux_free,
"nbio_linux",
};
#else
nbio_intf_t nbio_linux = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"nbio_linux",
};
#endif

268
file/nbio/nbio_stdio.c Normal file
View File

@ -0,0 +1,268 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nbio_stdio.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <file/nbio.h>
#include <encodings/utf.h>
/* Assume W-functions do not work below Win2K and Xbox platforms */
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
struct nbio_stdio_t
{
FILE* f;
void* data;
size_t progress;
size_t len;
/*
* possible values:
* NBIO_READ, NBIO_WRITE - obvious
* -1 - currently doing nothing
* -2 - the pointer was reallocated since the last operation
*/
signed char op;
signed char mode;
};
#if !defined(_WIN32) || defined(LEGACY_WIN32)
static const char *stdio_modes[] = { "rb", "wb", "r+b", "rb", "wb", "r+b" };
#else
static const wchar_t *stdio_modes[] = { L"rb", L"wb", L"r+b", L"rb", L"wb", L"r+b" };
#endif
static void *nbio_stdio_open(const char * filename, unsigned mode)
{
void *buf = NULL;
struct nbio_stdio_t* handle = NULL;
size_t len = 0;
#if !defined(_WIN32) || defined(LEGACY_WIN32)
FILE* f = fopen(filename, stdio_modes[mode]);
#else
wchar_t *filename_wide = utf8_to_utf16_string_alloc(filename);
FILE* f = _wfopen(filename_wide, stdio_modes[mode]);
if (filename_wide)
free(filename_wide);
#endif
if (!f)
return NULL;
handle = (struct nbio_stdio_t*)malloc(sizeof(struct nbio_stdio_t));
if (!handle)
goto error;
handle->f = f;
switch (mode)
{
case NBIO_WRITE:
case BIO_WRITE:
break;
default:
fseek(handle->f, 0, SEEK_END);
len = ftell(handle->f);
break;
}
handle->mode = mode;
if (len)
buf = malloc(len);
if (len && !buf)
goto error;
handle->data = buf;
handle->len = len;
handle->progress = handle->len;
handle->op = -2;
return handle;
error:
if (handle)
free(handle);
fclose(f);
return NULL;
}
static void nbio_stdio_begin_read(void *data)
{
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
if (!handle)
return;
if (handle->op >= 0)
{
puts("ERROR - attempted file read operation while busy");
abort();
}
fseek(handle->f, 0, SEEK_SET);
handle->op = NBIO_READ;
handle->progress = 0;
}
static void nbio_stdio_begin_write(void *data)
{
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
if (!handle)
return;
if (handle->op >= 0)
{
puts("ERROR - attempted file write operation while busy");
abort();
}
fseek(handle->f, 0, SEEK_SET);
handle->op = NBIO_WRITE;
handle->progress = 0;
}
static bool nbio_stdio_iterate(void *data)
{
size_t amount = 65536;
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
if (!handle)
return false;
if (amount > handle->len - handle->progress)
amount = handle->len - handle->progress;
switch (handle->op)
{
case NBIO_READ:
if (handle->mode == BIO_READ)
{
amount = handle->len;
fread((char*)handle->data, 1, amount, handle->f);
}
else
fread((char*)handle->data + handle->progress, 1, amount, handle->f);
break;
case NBIO_WRITE:
if (handle->mode == BIO_WRITE)
{
size_t written = 0;
amount = handle->len;
written = fwrite((char*)handle->data, 1, amount, handle->f);
if (written != amount)
return false;
}
else
fwrite((char*)handle->data + handle->progress, 1, amount, handle->f);
break;
}
handle->progress += amount;
if (handle->progress == handle->len)
handle->op = -1;
return (handle->op < 0);
}
static void nbio_stdio_resize(void *data, size_t len)
{
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
if (!handle)
return;
if (handle->op >= 0)
{
puts("ERROR - attempted file resize operation while busy");
abort();
}
if (len < handle->len)
{
puts("ERROR - attempted file shrink operation, not implemented");
abort();
}
handle->len = len;
handle->data = realloc(handle->data, handle->len);
handle->op = -1;
handle->progress = handle->len;
}
static void *nbio_stdio_get_ptr(void *data, size_t* len)
{
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
if (!handle)
return NULL;
if (len)
*len = handle->len;
if (handle->op == -1)
return handle->data;
return NULL;
}
static void nbio_stdio_cancel(void *data)
{
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
if (!handle)
return;
handle->op = -1;
handle->progress = handle->len;
}
static void nbio_stdio_free(void *data)
{
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
if (!handle)
return;
if (handle->op >= 0)
{
puts("ERROR - attempted free() while busy");
abort();
}
fclose(handle->f);
free(handle->data);
handle->f = NULL;
handle->data = NULL;
free(handle);
}
nbio_intf_t nbio_stdio = {
nbio_stdio_open,
nbio_stdio_begin_read,
nbio_stdio_begin_write,
nbio_stdio_iterate,
nbio_stdio_resize,
nbio_stdio_get_ptr,
nbio_stdio_cancel,
nbio_stdio_free,
"nbio_stdio",
};

191
file/nbio/nbio_unixmmap.c Normal file
View File

@ -0,0 +1,191 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nbio_unixmmap.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <file/nbio.h>
#if defined(HAVE_MMAP) && defined(BSD)
#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/mman.h>
#ifdef __APPLE__
#ifndef O_CLOEXEC
#define O_CLOEXEC 0x1000000
#endif
#else
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#endif
struct nbio_mmap_unix_t
{
int fd;
int map_flags;
size_t len;
void* ptr;
};
static void *nbio_mmap_unix_open(const char * filename, unsigned mode)
{
static const int o_flags[] = { O_RDONLY, O_RDWR|O_CREAT|O_TRUNC, O_RDWR, O_RDONLY, O_RDWR|O_CREAT|O_TRUNC };
static const int map_flags[] = { PROT_READ, PROT_WRITE|PROT_READ, PROT_WRITE|PROT_READ, PROT_READ, PROT_WRITE|PROT_READ };
size_t len;
void* ptr = NULL;
struct nbio_mmap_unix_t* handle = NULL;
int fd = open(filename, o_flags[mode]|O_CLOEXEC, 0644);
if (fd < 0)
return NULL;
len = lseek(fd, 0, SEEK_END);
if (len != 0)
ptr = mmap(NULL, len, map_flags[mode], MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
{
close(fd);
return NULL;
}
handle = malloc(sizeof(struct nbio_mmap_unix_t));
handle->fd = fd;
handle->map_flags = map_flags[mode];
handle->len = len;
handle->ptr = ptr;
return handle;
}
static void nbio_mmap_unix_begin_read(void *data)
{
/* not needed */
}
static void nbio_mmap_unix_begin_write(void *data)
{
/* not needed */
}
static bool nbio_mmap_unix_iterate(void *data)
{
return true; /* not needed */
}
static void nbio_mmap_unix_resize(void *data, size_t len)
{
struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
if (!handle)
return;
if (len < handle->len)
{
/* this works perfectly fine if this check is removed, but it
* won't work on other nbio implementations */
/* therefore, it's blocked so nobody accidentally relies on it */
puts("ERROR - attempted file shrink operation, not implemented");
abort();
}
if (ftruncate(handle->fd, len) != 0)
{
puts("ERROR - couldn't resize file (ftruncate)");
abort(); /* this one returns void and I can't find any other
way for it to report failure */
}
munmap(handle->ptr, handle->len);
handle->ptr = mmap(NULL, len, handle->map_flags, MAP_SHARED, handle->fd, 0);
handle->len = len;
if (handle->ptr == MAP_FAILED)
{
puts("ERROR - couldn't resize file (mmap)");
abort();
}
}
static void *nbio_mmap_unix_get_ptr(void *data, size_t* len)
{
struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
if (!handle)
return NULL;
if (len)
*len = handle->len;
return handle->ptr;
}
static void nbio_mmap_unix_cancel(void *data)
{
/* not needed */
}
static void nbio_mmap_unix_free(void *data)
{
struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
if (!handle)
return;
close(handle->fd);
munmap(handle->ptr, handle->len);
free(handle);
}
nbio_intf_t nbio_mmap_unix = {
nbio_mmap_unix_open,
nbio_mmap_unix_begin_read,
nbio_mmap_unix_begin_write,
nbio_mmap_unix_iterate,
nbio_mmap_unix_resize,
nbio_mmap_unix_get_ptr,
nbio_mmap_unix_cancel,
nbio_mmap_unix_free,
"nbio_mmap_unix",
};
#else
nbio_intf_t nbio_mmap_unix = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"nbio_mmap_unix",
};
#endif

View File

@ -0,0 +1,224 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nbio_windowsmmap.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <file/nbio.h>
#if defined(_WIN32) && !defined(_XBOX)
#include <stdio.h>
#include <stdlib.h>
#include <encodings/utf.h>
#include <windows.h>
/* Assume W-functions do not work below Win2K and Xbox platforms */
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
#ifndef FILE_SHARE_ALL
#define FILE_SHARE_ALL (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)
#endif
struct nbio_mmap_win32_t
{
HANDLE file;
bool is_write;
size_t len;
void* ptr;
};
static void *nbio_mmap_win32_open(const char * filename, unsigned mode)
{
static const DWORD dispositions[] = { OPEN_EXISTING, CREATE_ALWAYS, OPEN_ALWAYS, OPEN_EXISTING, CREATE_ALWAYS };
HANDLE mem;
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
LARGE_INTEGER len;
#else
SIZE_T len;
#endif
struct nbio_mmap_win32_t* handle = NULL;
void* ptr = NULL;
bool is_write = (mode == NBIO_WRITE || mode == NBIO_UPDATE || mode == BIO_WRITE);
DWORD access = (is_write ? GENERIC_READ|GENERIC_WRITE : GENERIC_READ);
#if !defined(_WIN32) || defined(LEGACY_WIN32)
HANDLE file = CreateFile(filename, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL);
#else
wchar_t *filename_wide = utf8_to_utf16_string_alloc(filename);
HANDLE file = CreateFileW(filename_wide, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL);
if (filename_wide)
free(filename_wide);
#endif
if (file == INVALID_HANDLE_VALUE)
return NULL;
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
/* GetFileSizeEx is new for Windows 2000 */
GetFileSizeEx(file, &len);
mem = CreateFileMapping(file, NULL, is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
ptr = MapViewOfFile(mem, is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len.QuadPart);
#else
GetFileSize(file, &len);
mem = CreateFileMapping(file, NULL, is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
ptr = MapViewOfFile(mem, is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len);
#endif
CloseHandle(mem);
handle = (struct nbio_mmap_win32_t*)malloc(sizeof(struct nbio_mmap_win32_t));
handle->file = file;
handle->is_write = is_write;
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
handle->len = len.QuadPart;
#else
handle->len = len;
#endif
handle->ptr = ptr;
return handle;
}
static void nbio_mmap_win32_begin_read(void *data)
{
/* not needed */
}
static void nbio_mmap_win32_begin_write(void *data)
{
/* not needed */
}
static bool nbio_mmap_win32_iterate(void *data)
{
/* not needed */
return true;
}
static void nbio_mmap_win32_resize(void *data, size_t len)
{
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
LARGE_INTEGER len_li;
#else
SIZE_T len_li;
#endif
HANDLE mem;
struct nbio_mmap_win32_t* handle = (struct nbio_mmap_win32_t*)data;
if (!handle)
return;
if (len < handle->len)
{
/* this works perfectly fine if this check is removed,
* but it won't work on other nbio implementations */
/* therefore, it's blocked so nobody accidentally
* relies on it. */
puts("ERROR - attempted file shrink operation, not implemented");
abort();
}
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
/* SetFilePointerEx is new for Windows 2000 */
len_li.QuadPart = len;
SetFilePointerEx(handle->file, len_li, NULL, FILE_BEGIN);
#else
len_li = len;
SetFilePointer(handle->file, len_li, NULL, FILE_BEGIN);
#endif
if (!SetEndOfFile(handle->file))
{
puts("ERROR - couldn't resize file (SetEndOfFile)");
abort(); /* this one returns void and I can't find any other way for it to report failure */
}
handle->len = len;
UnmapViewOfFile(handle->ptr);
mem = CreateFileMapping(handle->file, NULL, handle->is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
handle->ptr = MapViewOfFile(mem, handle->is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len);
CloseHandle(mem);
if (!handle->ptr)
{
puts("ERROR - couldn't resize file (MapViewOfFile)");
abort();
}
}
static void *nbio_mmap_win32_get_ptr(void *data, size_t* len)
{
struct nbio_mmap_win32_t* handle = (struct nbio_mmap_win32_t*)data;
if (!handle)
return NULL;
if (len)
*len = handle->len;
return handle->ptr;
}
static void nbio_mmap_win32_cancel(void *data)
{
/* not needed */
}
static void nbio_mmap_win32_free(void *data)
{
struct nbio_mmap_win32_t* handle = (struct nbio_mmap_win32_t*)data;
if (!handle)
return;
CloseHandle(handle->file);
UnmapViewOfFile(handle->ptr);
free(handle);
}
nbio_intf_t nbio_mmap_win32 = {
nbio_mmap_win32_open,
nbio_mmap_win32_begin_read,
nbio_mmap_win32_begin_write,
nbio_mmap_win32_iterate,
nbio_mmap_win32_resize,
nbio_mmap_win32_get_ptr,
nbio_mmap_win32_cancel,
nbio_mmap_win32_free,
"nbio_mmap_win32",
};
#else
nbio_intf_t nbio_mmap_win32 = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"nbio_mmap_win32",
};
#endif

300
file/retro_dirent.c Normal file
View File

@ -0,0 +1,300 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_dirent.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <retro_common.h>
#include <boolean.h>
#include <retro_dirent.h>
#include <encodings/utf.h>
#include <compat/strl.h>
#include <string/stdstring.h>
#if defined(_WIN32)
# ifdef _MSC_VER
# define setmode _setmode
# endif
#include <sys/stat.h>
# ifdef _XBOX
# include <xtl.h>
# define INVALID_FILE_ATTRIBUTES -1
# else
# include <io.h>
# include <fcntl.h>
# include <direct.h>
# include <windows.h>
# endif
#elif defined(VITA)
# include <psp2/io/fcntl.h>
# include <psp2/io/dirent.h>
#include <psp2/io/stat.h>
#else
# if defined(PSP)
# include <pspiofilemgr.h>
# endif
# include <sys/types.h>
# include <sys/stat.h>
# include <dirent.h>
# include <unistd.h>
#endif
#ifdef __CELLOS_LV2__
#include <cell/cell_fs.h>
#endif
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
#include <unistd.h> /* stat() is defined here */
#endif
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
struct RDIR
{
#if defined(_WIN32)
#if defined(LEGACY_WIN32)
WIN32_FIND_DATA entry;
#else
WIN32_FIND_DATAW entry;
#endif
HANDLE directory;
bool next;
char path[PATH_MAX_LENGTH];
#elif defined(VITA) || defined(PSP)
SceUID directory;
SceIoDirent entry;
#elif defined(__CELLOS_LV2__)
CellFsErrno error;
int directory;
CellFsDirent entry;
#else
DIR *directory;
const struct dirent *entry;
#endif
};
struct RDIR *retro_opendir(const char *name)
{
#if defined(_WIN32)
char path_buf[1024];
char *path_local = NULL;
wchar_t *path_wide = NULL;
unsigned path_len;
#endif
struct RDIR *rdir;
/*Reject null or empty string paths*/
if (!name||(*name==0))
return NULL;
/*Allocate RDIR struct. Tidied later with retro_closedir*/
rdir = (struct RDIR*)calloc(1, sizeof(*rdir));
if (!rdir)
return NULL;
#if defined(_WIN32)
(void)path_wide;
(void)path_local;
path_buf[0] = '\0';
path_len = strlen(name);
/* Non-NT platforms don't like extra slashes in the path */
if (name[path_len - 1] == '\\')
snprintf(path_buf, sizeof(path_buf), "%s*", name);
else
snprintf(path_buf, sizeof(path_buf), "%s\\*", name);
#if defined(LEGACY_WIN32)
path_local = utf8_to_local_string_alloc(path_buf);
rdir->directory = FindFirstFile(path_local, &rdir->entry);
if (path_local)
free(path_local);
#else
path_wide = utf8_to_utf16_string_alloc(path_buf);
rdir->directory = FindFirstFileW(path_wide, &rdir->entry);
if (path_wide)
free(path_wide);
#endif
#elif defined(VITA) || defined(PSP)
rdir->directory = sceIoDopen(name);
#elif defined(_3DS)
rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
rdir->entry = NULL;
#elif defined(__CELLOS_LV2__)
rdir->error = cellFsOpendir(name, &rdir->directory);
#else
rdir->directory = opendir(name);
rdir->entry = NULL;
#endif
if (rdir->directory)
return rdir;
free(rdir);
return NULL;
}
bool retro_dirent_error(struct RDIR *rdir)
{
#if defined(_WIN32)
return (rdir->directory == INVALID_HANDLE_VALUE);
#elif defined(VITA) || defined(PSP)
return (rdir->directory < 0);
#elif defined(__CELLOS_LV2__)
return (rdir->error != CELL_FS_SUCCEEDED);
#else
return !(rdir->directory);
#endif
}
int retro_readdir(struct RDIR *rdir)
{
#if defined(_WIN32)
if(rdir->next)
#if defined(LEGACY_WIN32)
return (FindNextFile(rdir->directory, &rdir->entry) != 0);
#else
return (FindNextFileW(rdir->directory, &rdir->entry) != 0);
#endif
rdir->next = true;
return (rdir->directory != INVALID_HANDLE_VALUE);
#elif defined(VITA) || defined(PSP)
return (sceIoDread(rdir->directory, &rdir->entry) > 0);
#elif defined(__CELLOS_LV2__)
uint64_t nread;
rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread);
return (nread != 0);
#else
return ((rdir->entry = readdir(rdir->directory)) != NULL);
#endif
}
const char *retro_dirent_get_name(struct RDIR *rdir)
{
#if defined(_WIN32)
#if defined(LEGACY_WIN32)
char *name_local = local_to_utf8_string_alloc(rdir->entry.cFileName);
memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
strlcpy(rdir->entry.cFileName, name_local, sizeof(rdir->entry.cFileName));
if (name_local)
free(name_local);
#else
char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName);
memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName));
if (name)
free(name);
#endif
return (char*)rdir->entry.cFileName;
#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__)
return rdir->entry.d_name;
#else
return rdir->entry->d_name;
#endif
}
/**
*
* retro_dirent_is_dir:
* @rdir : pointer to the directory entry.
* @path : path to the directory entry.
*
* Is the directory listing entry a directory?
*
* Returns: true if directory listing entry is
* a directory, false if not.
*/
bool retro_dirent_is_dir(struct RDIR *rdir, const char *path)
{
#if defined(_WIN32)
const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
#elif defined(PSP) || defined(VITA)
const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
#if defined(PSP)
return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
#elif defined(VITA)
return SCE_S_ISDIR(entry->d_stat.st_mode);
#endif
#elif defined(__CELLOS_LV2__)
CellFsDirent *entry = (CellFsDirent*)&rdir->entry;
return (entry->d_type == CELL_FS_TYPE_DIRECTORY);
#else
struct stat buf;
#if defined(DT_DIR)
const struct dirent *entry = (const struct dirent*)rdir->entry;
if (entry->d_type == DT_DIR)
return true;
/* This can happen on certain file systems. */
if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
return false;
#endif
/* dirent struct doesn't have d_type, do it the slow way ... */
if (stat(path, &buf) < 0)
return false;
return S_ISDIR(buf.st_mode);
#endif
}
void retro_dirent_include_hidden(struct RDIR *rdir, bool include_hidden)
{
#ifdef _WIN32
if (include_hidden)
rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
else
rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
#endif
}
void retro_closedir(struct RDIR *rdir)
{
if (!rdir)
return;
#if defined(_WIN32)
if (rdir->directory != INVALID_HANDLE_VALUE)
FindClose(rdir->directory);
#elif defined(VITA) || defined(PSP)
sceIoDclose(rdir->directory);
#elif defined(__CELLOS_LV2__)
rdir->error = cellFsClosedir(rdir->directory);
#else
if (rdir->directory)
closedir(rdir->directory);
#endif
free(rdir);
}

623
formats/bmp/rbmp.c Normal file
View File

@ -0,0 +1,623 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rbmp.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Modified version of stb_image's BMP sources. */
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h> /* ptrdiff_t on osx */
#include <stdlib.h>
#include <string.h>
#include <retro_inline.h>
#include <formats/image.h>
#include <formats/rbmp.h>
/* truncate int to byte without warnings */
#define RBMP__BYTECAST(x) ((unsigned char) ((x) & 255))
#define RBMP_COMPUTE_Y(r, g, b) ((unsigned char) ((((r) * 77) + ((g) * 150) + (29 * (b))) >> 8))
typedef struct
{
uint32_t img_x;
uint32_t img_y;
int img_n;
int img_out_n;
int buflen;
unsigned char buffer_start[128];
unsigned char *img_buffer;
unsigned char *img_buffer_end;
unsigned char *img_buffer_original;
} rbmp__context;
struct rbmp
{
uint8_t *buff_data;
uint32_t *output_image;
};
static INLINE unsigned char rbmp__get8(rbmp__context *s)
{
if (s->img_buffer < s->img_buffer_end)
return *s->img_buffer++;
return 0;
}
static void rbmp__skip(rbmp__context *s, int n)
{
if (n < 0)
{
s->img_buffer = s->img_buffer_end;
return;
}
s->img_buffer += n;
}
static int rbmp__get16le(rbmp__context *s)
{
int z = rbmp__get8(s);
return z + (rbmp__get8(s) << 8);
}
static uint32_t rbmp__get32le(rbmp__context *s)
{
uint32_t z = rbmp__get16le(s);
return z + (rbmp__get16le(s) << 16);
}
static unsigned char *rbmp__convert_format(
unsigned char *data,
int img_n,
int req_comp,
unsigned int x,
unsigned int y)
{
int i,j;
unsigned char *good = (unsigned char *)malloc(req_comp * x * y);
if (!good)
return NULL;
for (j=0; j < (int) y; ++j)
{
unsigned char *src = data + j * x * img_n ;
unsigned char *dest = good + j * x * req_comp;
switch (((img_n)*8+(req_comp)))
{
case ((1)*8+(2)):
for(i=x-1; i >= 0; --i, src += 1, dest += 2)
dest[0]=src[0], dest[1]=255;
break;
case ((1)*8+(3)):
for(i=x-1; i >= 0; --i, src += 1, dest += 3)
dest[0]=dest[1]=dest[2]=src[0];
break;
case ((1)*8+(4)):
for(i=x-1; i >= 0; --i, src += 1, dest += 4)
dest[0]=dest[1]=dest[2]=src[0], dest[3]=255;
break;
case ((2)*8+(1)):
for(i=x-1; i >= 0; --i, src += 2, dest += 1)
dest[0]=src[0];
break;
case ((2)*8+(3)):
for(i=x-1; i >= 0; --i, src += 2, dest += 3)
dest[0]=dest[1]=dest[2]=src[0];
break;
case ((2)*8+(4)):
for(i=x-1; i >= 0; --i, src += 2, dest += 4)
dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];
break;
case ((3)*8+(4)):
for(i=x-1; i >= 0; --i, src += 3, dest += 4)
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255;
break;
case ((3)*8+(1)):
for(i=x-1; i >= 0; --i, src += 3, dest += 1)
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
break;
case ((3)*8+(2)):
for(i=x-1; i >= 0; --i, src += 3, dest += 2)
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = 255;
break;
case ((4)*8+(1)):
for(i=x-1; i >= 0; --i, src += 4, dest += 1)
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
break;
case ((4)*8+(2)):
for(i=x-1; i >= 0; --i, src += 4, dest += 2)
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = src[3];
break;
case ((4)*8+(3)):
for(i=x-1; i >= 0; --i, src += 4, dest += 3)
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];
break;
default:
break;
}
}
return good;
}
/* Microsoft/Windows BMP image */
/* returns 0..31 for the highest set bit */
static int rbmp__high_bit(unsigned int z)
{
int n=0;
if (z == 0)
return -1;
if (z >= 0x10000) n += 16, z >>= 16;
if (z >= 0x00100) n += 8, z >>= 8;
if (z >= 0x00010) n += 4, z >>= 4;
if (z >= 0x00004) n += 2, z >>= 2;
if (z >= 0x00002) n += 1, z >>= 1;
return n;
}
static int rbmp__bitcount(unsigned int a)
{
a = (a & 0x55555555) + ((a >> 1) & 0x55555555); /* max 2 */
a = (a & 0x33333333) + ((a >> 2) & 0x33333333); /* max 4 */
a = (a + (a >> 4)) & 0x0f0f0f0f; /* max 8 per 4, now 8 bits */
a = (a + (a >> 8)); /* max 16 per 8 bits */
a = (a + (a >> 16)); /* max 32 per 8 bits */
return a & 0xff;
}
static int rbmp__shiftsigned(int v, int shift, int bits)
{
int result;
int z=0;
if (shift < 0)
v <<= -shift;
else
v >>= shift;
result = v;
z = bits;
while (z < 8)
{
result += v >> z;
z += bits;
}
return result;
}
static unsigned char *rbmp__bmp_load(rbmp__context *s, unsigned *x, unsigned *y,
int *comp, int req_comp)
{
unsigned char *out;
int bpp, flip_vertically, pad, target, offset, hsz;
int psize=0,i,j,width;
unsigned int mr=0,mg=0,mb=0,ma=0;
/* Corrupt BMP? */
if (rbmp__get8(s) != 'B' || rbmp__get8(s) != 'M')
return 0;
rbmp__get32le(s); /* discard filesize */
rbmp__get16le(s); /* discard reserved */
rbmp__get16le(s); /* discard reserved */
offset = rbmp__get32le(s);
hsz = rbmp__get32le(s);
/* BMP type not supported? */
if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124)
return 0;
if (hsz == 12)
{
s->img_x = rbmp__get16le(s);
s->img_y = rbmp__get16le(s);
}
else
{
s->img_x = rbmp__get32le(s);
s->img_y = rbmp__get32le(s);
}
/* Bad BMP? */
if (rbmp__get16le(s) != 1)
return 0;
bpp = rbmp__get16le(s);
/* BMP 1-bit type not supported? */
if (bpp == 1)
return 0;
flip_vertically = ((int) s->img_y) > 0;
s->img_y = abs((int) s->img_y);
if (hsz == 12)
{
if (bpp < 24)
psize = (offset - 14 - 24) / 3;
}
else
{
int compress = rbmp__get32le(s);
/* BMP RLE type not supported? */
if (compress == 1 || compress == 2)
return 0;
rbmp__get32le(s); /* discard sizeof */
rbmp__get32le(s); /* discard hres */
rbmp__get32le(s); /* discard vres */
rbmp__get32le(s); /* discard colors used */
rbmp__get32le(s); /* discard max important */
if (hsz == 40 || hsz == 56)
{
if (hsz == 56)
{
rbmp__get32le(s);
rbmp__get32le(s);
rbmp__get32le(s);
rbmp__get32le(s);
}
if (bpp == 16 || bpp == 32)
{
switch (compress)
{
case 0:
#if 0
if (bpp == 32)
{
mr = 0xffu << 16;
mg = 0xffu << 8;
mb = 0xffu << 0;
ma = 0xffu << 24;
}
else
{
mr = 31u << 10;
mg = 31u << 5;
mb = 31u << 0;
}
#endif
break;
case 3:
mr = rbmp__get32le(s);
mg = rbmp__get32le(s);
mb = rbmp__get32le(s);
/* not documented, but generated by
* Photoshop and handled by MS Paint */
/* Bad BMP ?*/
if (mr == mg && mg == mb)
return 0;
break;
default:
#if 0
mr = mg = mb = 0;
#endif
break;
}
/* Bad BMP? */
return 0;
}
}
else
{
mr = rbmp__get32le(s);
mg = rbmp__get32le(s);
mb = rbmp__get32le(s);
ma = rbmp__get32le(s);
rbmp__get32le(s); /* Discard color space */
for (i=0; i < 12; ++i)
rbmp__get32le(s); /* Discard color space parameters */
if (hsz == 124)
{
rbmp__get32le(s); /* Discard rendering intent */
rbmp__get32le(s); /* Discard offset of profile data */
rbmp__get32le(s); /* Discard size of profile data */
rbmp__get32le(s); /* Discard reserved */
}
}
if (bpp < 16)
psize = (offset - 14 - hsz) >> 2;
}
s->img_n = ma ? 4 : 3;
if (req_comp && req_comp >= 3) /* We can directly decode 3 or 4 */
target = req_comp;
else
target = s->img_n; /* If they want monochrome, we'll post-convert */
out = (unsigned char *) malloc(target * s->img_x * s->img_y);
if (!out)
return 0;
if (bpp < 16)
{
unsigned char pal[256][4];
int z=0;
/* Corrupt BMP? */
if (psize == 0 || psize > 256)
{
free(out);
return 0;
}
for (i=0; i < psize; ++i)
{
pal[i][2] = rbmp__get8(s);
pal[i][1] = rbmp__get8(s);
pal[i][0] = rbmp__get8(s);
if (hsz != 12) rbmp__get8(s);
pal[i][3] = 255;
}
rbmp__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
if (bpp == 4)
width = (s->img_x + 1) >> 1;
else if (bpp == 8)
width = s->img_x;
else
{
/* Corrupt BMP */
free(out);
return 0;
}
pad = (-width)&3;
for (j=0; j < (int) s->img_y; ++j)
{
for (i=0; i < (int) s->img_x; i += 2)
{
int v=rbmp__get8(s),v2=0;
if (bpp == 4)
{
v2 = v & 15;
v >>= 4;
}
out[z++] = pal[v][0];
out[z++] = pal[v][1];
out[z++] = pal[v][2];
if (target == 4)
out[z++] = 255;
if (i+1 == (int)s->img_x)
break;
v = (bpp == 8) ? rbmp__get8(s) : v2;
out[z++] = pal[v][0];
out[z++] = pal[v][1];
out[z++] = pal[v][2];
if (target == 4)
out[z++] = 255;
}
rbmp__skip(s, pad);
}
}
else
{
int rshift=0;
int gshift=0;
int bshift=0;
int ashift=0;
int rcount=0;
int gcount=0;
int bcount=0;
int acount=0;
int z = 0;
int easy=0;
rbmp__skip(s, offset - 14 - hsz);
if (bpp == 24)
width = 3 * s->img_x;
else if (bpp == 16)
width = 2*s->img_x;
else /* bpp = 32 and pad = 0 */
width=0;
pad = (-width) & 3;
switch (bpp)
{
case 24:
easy = 1;
break;
case 32:
if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
easy = 2;
break;
default:
break;
}
if (!easy)
{
/* Corrupt BMP? */
if (!mr || !mg || !mb)
{
free(out);
return 0;
}
/* right shift amt to put high bit in position #7 */
rshift = rbmp__high_bit(mr)-7;
rcount = rbmp__bitcount(mr);
gshift = rbmp__high_bit(mg)-7;
gcount = rbmp__bitcount(mg);
bshift = rbmp__high_bit(mb)-7;
bcount = rbmp__bitcount(mb);
ashift = rbmp__high_bit(ma)-7;
acount = rbmp__bitcount(ma);
}
for (j=0; j < (int) s->img_y; ++j)
{
if (easy)
{
for (i=0; i < (int) s->img_x; ++i)
{
unsigned char a;
out[z+2] = rbmp__get8(s);
out[z+1] = rbmp__get8(s);
out[z+0] = rbmp__get8(s);
z += 3;
a = (easy == 2 ? rbmp__get8(s) : 255);
if (target == 4)
out[z++] = a;
}
}
else
{
for (i=0; i < (int) s->img_x; ++i)
{
uint32_t v = (bpp == 16 ? (uint32_t) rbmp__get16le(s) : rbmp__get32le(s));
int a;
out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mr, rshift, rcount));
out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mg, gshift, gcount));
out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mb, bshift, bcount));
a = (ma ? rbmp__shiftsigned(v & ma, ashift, acount) : 255);
if (target == 4)
out[z++] = RBMP__BYTECAST(a);
}
}
rbmp__skip(s, pad);
}
}
if (flip_vertically)
{
unsigned char t;
for (j=0; j < (int) s->img_y>>1; ++j)
{
unsigned char *p1 = out + j *s->img_x*target;
unsigned char *p2 = out + (s->img_y-1-j)*s->img_x*target;
for (i=0; i < (int) s->img_x*target; ++i)
{
t = p1[i], p1[i] = p2[i], p2[i] = t;
}
}
}
if (
req_comp
&& (req_comp >= 1 && req_comp <= 4)
&& (req_comp != target))
{
unsigned char *tmp = rbmp__convert_format(out, target, req_comp, s->img_x, s->img_y);
free(out);
out = NULL;
if (!tmp)
return NULL;
out = tmp;
}
*x = s->img_x;
*y = s->img_y;
if (comp)
*comp = s->img_n;
return out;
}
static unsigned char *rbmp_load_from_memory(unsigned char const *buffer, int len,
unsigned *x, unsigned *y, int *comp, int req_comp)
{
rbmp__context s;
s.img_buffer = (unsigned char*)buffer;
s.img_buffer_original = (unsigned char*)buffer;
s.img_buffer_end = (unsigned char*)buffer+len;
return rbmp__bmp_load(&s,x,y,comp,req_comp);
}
static void rbmp_convert_frame(uint32_t *frame, unsigned width, unsigned height)
{
uint32_t *end = frame + (width * height * sizeof(uint32_t))/4;
while(frame < end)
{
uint32_t pixel = *frame;
*frame = (pixel & 0xff00ff00) | ((pixel << 16) & 0x00ff0000) | ((pixel >> 16) & 0xff);
frame++;
}
}
int rbmp_process_image(rbmp_t *rbmp, void **buf_data,
size_t size, unsigned *width, unsigned *height)
{
int comp;
if (!rbmp)
return IMAGE_PROCESS_ERROR;
rbmp->output_image = (uint32_t*)rbmp_load_from_memory(rbmp->buff_data,
(int)size, width, height, &comp, 4);
*buf_data = rbmp->output_image;
rbmp_convert_frame(rbmp->output_image, *width, *height);
return IMAGE_PROCESS_END;
}
bool rbmp_set_buf_ptr(rbmp_t *rbmp, void *data)
{
if (!rbmp)
return false;
rbmp->buff_data = (uint8_t*)data;
return true;
}
void rbmp_free(rbmp_t *rbmp)
{
if (!rbmp)
return;
free(rbmp);
}
rbmp_t *rbmp_alloc(void)
{
rbmp_t *rbmp = (rbmp_t*)calloc(1, sizeof(*rbmp));
if (!rbmp)
return NULL;
return rbmp;
}

232
formats/bmp/rbmp_encode.c Normal file
View File

@ -0,0 +1,232 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rbmp_encode.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <streams/file_stream.h>
#include <formats/rbmp.h>
static bool write_header_bmp(RFILE *file, unsigned width, unsigned height, bool is32bpp)
{
uint8_t header[54];
unsigned line_size = (width * (is32bpp?4:3) + 3) & ~3;
unsigned size = line_size * height + 54;
unsigned size_array = line_size * height;
/* Generic BMP stuff. */
/* signature */
header[0] = 'B';
header[1] = 'M';
/* file size */
header[2] = (uint8_t)(size >> 0);
header[3] = (uint8_t)(size >> 8);
header[4] = (uint8_t)(size >> 16);
header[5] = (uint8_t)(size >> 24);
/* reserved */
header[6] = 0;
header[7] = 0;
header[8] = 0;
header[9] = 0;
/* offset */
header[10] = 54;
header[11] = 0;
header[12] = 0;
header[13] = 0;
/* DIB size */
header[14] = 40;
header[15] = 0;
header[16] = 0;
header[17] = 0;
/* Width */
header[18] = (uint8_t)(width >> 0);
header[19] = (uint8_t)(width >> 8);
header[20] = (uint8_t)(width >> 16);
header[21] = (uint8_t)(width >> 24);
/* Height */
header[22] = (uint8_t)(height >> 0);
header[23] = (uint8_t)(height >> 8);
header[24] = (uint8_t)(height >> 16);
header[25] = (uint8_t)(height >> 24);
/* Color planes */
header[26] = 1;
header[27] = 0;
/* Bits per pixel */
header[28] = is32bpp?32:24;
header[29] = 0;
/* Compression method */
header[30] = 0;
header[31] = 0;
header[32] = 0;
header[33] = 0;
/* Image data size */
header[34] = (uint8_t)(size_array >> 0);
header[35] = (uint8_t)(size_array >> 8);
header[36] = (uint8_t)(size_array >> 16);
header[37] = (uint8_t)(size_array >> 24);
/* Horizontal resolution */
header[38] = 19;
header[39] = 11;
header[40] = 0;
header[41] = 0;
/* Vertical resolution */
header[42] = 19;
header[43] = 11;
header[44] = 0;
header[45] = 0;
/* Palette size */
header[46] = 0;
header[47] = 0;
header[48] = 0;
header[49] = 0;
/* Important color count */
header[50] = 0;
header[51] = 0;
header[52] = 0;
header[53] = 0;
return filestream_write(file, header, sizeof(header)) == sizeof(header);
}
static void dump_line_565_to_24(uint8_t *line, const uint16_t *src, unsigned width)
{
unsigned i;
for (i = 0; i < width; i++)
{
uint16_t pixel = *src++;
uint8_t b = (pixel >> 0) & 0x1f;
uint8_t g = (pixel >> 5) & 0x3f;
uint8_t r = (pixel >> 11) & 0x1f;
*line++ = (b << 3) | (b >> 2);
*line++ = (g << 2) | (g >> 4);
*line++ = (r << 3) | (r >> 2);
}
}
static void dump_line_32_to_24(uint8_t *line, const uint32_t *src, unsigned width)
{
unsigned i;
for (i = 0; i < width; i++)
{
uint32_t pixel = *src++;
*line++ = (pixel >> 0) & 0xff;
*line++ = (pixel >> 8) & 0xff;
*line++ = (pixel >> 16) & 0xff;
}
}
static void dump_content(RFILE *file, const void *frame,
int width, int height, int pitch, enum rbmp_source_type type)
{
int j;
size_t line_size;
uint8_t *line = NULL;
int bytes_per_pixel = (type==RBMP_SOURCE_TYPE_ARGB8888?4:3);
union
{
const uint8_t *u8;
const uint16_t *u16;
const uint32_t *u32;
} u;
u.u8 = (const uint8_t*)frame;
line_size = (width * bytes_per_pixel + 3) & ~3;
switch (type)
{
case RBMP_SOURCE_TYPE_BGR24:
{
/* BGR24 byte order input matches output. Can directly copy, but... need to make sure we pad it. */
uint32_t zeros = 0;
int pad = (int)(line_size-pitch);
for (j = 0; j < height; j++, u.u8 += pitch)
{
filestream_write(file, u.u8, pitch);
if(pad != 0)
filestream_write(file, &zeros, pad);
}
}
break;
case RBMP_SOURCE_TYPE_ARGB8888:
/* ARGB8888 byte order input matches output. Can directly copy. */
for (j = 0; j < height; j++, u.u8 += pitch)
filestream_write(file, u.u8, line_size);
return;
default:
break;
}
/* allocate line buffer, and initialize the final four bytes to zero, for deterministic padding */
line = (uint8_t*)malloc(line_size);
if (!line)
return;
*(uint32_t*)(line + line_size - 4) = 0;
switch (type)
{
case RBMP_SOURCE_TYPE_XRGB888:
for (j = 0; j < height; j++, u.u8 += pitch)
{
dump_line_32_to_24(line, u.u32, width);
filestream_write(file, line, line_size);
}
break;
case RBMP_SOURCE_TYPE_RGB565:
for (j = 0; j < height; j++, u.u8 += pitch)
{
dump_line_565_to_24(line, u.u16, width);
filestream_write(file, line, line_size);
}
break;
default:
break;
}
/* Free allocated line buffer */
free(line);
}
bool rbmp_save_image(
const char *filename,
const void *frame,
unsigned width, unsigned height,
unsigned pitch, enum rbmp_source_type type)
{
bool ret = false;
RFILE *file = filestream_open(filename,
RETRO_VFS_FILE_ACCESS_WRITE,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
return false;
ret = write_header_bmp(file, width, height, type==RBMP_SOURCE_TYPE_ARGB8888);
if (ret)
dump_content(file, frame, width, height, pitch, type);
filestream_close(file);
return ret;
}

315
formats/image_texture.c Normal file
View File

@ -0,0 +1,315 @@
/* Copyright (C) 2010-2018 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;
}

286
formats/image_transfer.c Normal file
View File

@ -0,0 +1,286 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (image_transfer.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_RPNG
#include <formats/rpng.h>
#endif
#ifdef HAVE_RJPEG
#include <formats/rjpeg.h>
#endif
#ifdef HAVE_RTGA
#include <formats/rtga.h>
#endif
#ifdef HAVE_RBMP
#include <formats/rbmp.h>
#endif
#include <formats/image.h>
void image_transfer_free(void *data, enum image_type_enum type)
{
switch (type)
{
case IMAGE_TYPE_TGA:
#ifdef HAVE_RTGA
rtga_free((rtga_t*)data);
#endif
break;
case IMAGE_TYPE_PNG:
{
#ifdef HAVE_RPNG
rpng_t *rpng = (rpng_t*)data;
if (rpng)
rpng_free(rpng);
#endif
}
break;
case IMAGE_TYPE_JPEG:
#ifdef HAVE_RJPEG
rjpeg_free((rjpeg_t*)data);
#endif
break;
case IMAGE_TYPE_BMP:
#ifdef HAVE_RBMP
rbmp_free((rbmp_t*)data);
#endif
break;
case IMAGE_TYPE_NONE:
break;
}
}
void *image_transfer_new(enum image_type_enum type)
{
switch (type)
{
case IMAGE_TYPE_PNG:
#ifdef HAVE_RPNG
return rpng_alloc();
#else
break;
#endif
case IMAGE_TYPE_JPEG:
#ifdef HAVE_RJPEG
return rjpeg_alloc();
#else
break;
#endif
case IMAGE_TYPE_TGA:
#ifdef HAVE_RTGA
return rtga_alloc();
#else
break;
#endif
case IMAGE_TYPE_BMP:
#ifdef HAVE_RBMP
return rbmp_alloc();
#else
break;
#endif
default:
break;
}
return NULL;
}
bool image_transfer_start(void *data, enum image_type_enum type)
{
switch (type)
{
case IMAGE_TYPE_PNG:
#ifdef HAVE_RPNG
if (!rpng_start((rpng_t*)data))
break;
return true;
#else
break;
#endif
case IMAGE_TYPE_JPEG:
#ifdef HAVE_RJPEG
return true;
#else
break;
#endif
case IMAGE_TYPE_TGA:
#ifdef HAVE_RTGA
return true;
#else
break;
#endif
case IMAGE_TYPE_BMP:
return true;
case IMAGE_TYPE_NONE:
break;
}
return false;
}
bool image_transfer_is_valid(
void *data,
enum image_type_enum type)
{
switch (type)
{
case IMAGE_TYPE_PNG:
#ifdef HAVE_RPNG
return rpng_is_valid((rpng_t*)data);
#else
break;
#endif
case IMAGE_TYPE_JPEG:
#ifdef HAVE_RJPEG
return true;
#else
break;
#endif
case IMAGE_TYPE_TGA:
#ifdef HAVE_RTGA
return true;
#else
break;
#endif
case IMAGE_TYPE_BMP:
return true;
case IMAGE_TYPE_NONE:
break;
}
return false;
}
void image_transfer_set_buffer_ptr(
void *data,
enum image_type_enum type,
void *ptr)
{
switch (type)
{
case IMAGE_TYPE_PNG:
#ifdef HAVE_RPNG
rpng_set_buf_ptr((rpng_t*)data, (uint8_t*)ptr);
#endif
break;
case IMAGE_TYPE_JPEG:
#ifdef HAVE_RJPEG
rjpeg_set_buf_ptr((rjpeg_t*)data, (uint8_t*)ptr);
#endif
break;
case IMAGE_TYPE_TGA:
#ifdef HAVE_RTGA
rtga_set_buf_ptr((rtga_t*)data, (uint8_t*)ptr);
#endif
break;
case IMAGE_TYPE_BMP:
#ifdef HAVE_RBMP
rbmp_set_buf_ptr((rbmp_t*)data, (uint8_t*)ptr);
#endif
break;
case IMAGE_TYPE_NONE:
break;
}
}
int image_transfer_process(
void *data,
enum image_type_enum type,
uint32_t **buf, size_t len,
unsigned *width, unsigned *height)
{
switch (type)
{
case IMAGE_TYPE_PNG:
#ifdef HAVE_RPNG
if (!rpng_is_valid((rpng_t*)data))
return IMAGE_PROCESS_ERROR;
return rpng_process_image(
(rpng_t*)data,
(void**)buf, len, width, height);
#else
break;
#endif
case IMAGE_TYPE_JPEG:
#ifdef HAVE_RJPEG
return rjpeg_process_image((rjpeg_t*)data,
(void**)buf, len, width, height);
#else
break;
#endif
case IMAGE_TYPE_TGA:
#ifdef HAVE_RTGA
return rtga_process_image((rtga_t*)data,
(void**)buf, len, width, height);
#else
break;
#endif
case IMAGE_TYPE_BMP:
#ifdef HAVE_RBMP
return rbmp_process_image((rbmp_t*)data,
(void**)buf, len, width, height);
#else
break;
#endif
case IMAGE_TYPE_NONE:
break;
}
return 0;
}
bool image_transfer_iterate(void *data, enum image_type_enum type)
{
switch (type)
{
case IMAGE_TYPE_PNG:
#ifdef HAVE_RPNG
if (!rpng_iterate_image((rpng_t*)data))
return false;
#endif
break;
case IMAGE_TYPE_JPEG:
#ifdef HAVE_RJPEG
return false;
#else
break;
#endif
case IMAGE_TYPE_TGA:
#ifdef HAVE_RTGA
return false;
#else
break;
#endif
case IMAGE_TYPE_BMP:
return false;
case IMAGE_TYPE_NONE:
return false;
}
return true;
}

2626
formats/jpeg/rjpeg.c Normal file

File diff suppressed because it is too large Load Diff

316
formats/json/jsonsax.c Normal file
View File

@ -0,0 +1,316 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (jsonsax.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <retro_inline.h>
#include <formats/jsonsax.h>
#ifdef JSONSAX_ERRORS
const char* jsonsax_errors[] =
{
"Ok",
"Interrupted",
"Missing key",
"Unterminated key",
"Missing value",
"Unterminated object",
"Unterminated array",
"Unterminated string",
"Invalid value"
};
#endif
typedef struct
{
const jsonsax_handlers_t* handlers;
const char* json;
void* ud;
jmp_buf env;
}
state_t;
static INLINE void skip_spaces( state_t* state )
{
while ( isspace( (unsigned char)*state->json ) )
state->json++;
}
static INLINE void skip_digits( state_t* state )
{
while ( isdigit( (unsigned char)*state->json ) )
state->json++;
}
#define HANDLE_0( event ) \
do { \
if ( state->handlers->event && state->handlers->event( state->ud ) ) \
longjmp( state->env, JSONSAX_INTERRUPTED ); \
} while ( 0 )
#define HANDLE_1( event, arg1 ) \
do { \
if ( state->handlers->event && state->handlers->event( state->ud, arg1 ) ) \
longjmp( state->env, JSONSAX_INTERRUPTED ); \
} while ( 0 )
#define HANDLE_2( event, arg1, arg2 ) \
do { \
if ( state->handlers->event && state->handlers->event( state->ud, arg1, arg2 ) ) \
longjmp( state->env, JSONSAX_INTERRUPTED ); \
} while ( 0 )
static void jsonx_parse_value(state_t* state);
static void jsonx_parse_object( state_t* state )
{
state->json++; /* we're sure the current character is a '{' */
skip_spaces( state );
HANDLE_0( start_object );
while ( *state->json != '}' )
{
const char *name = NULL;
if ( *state->json != '"' )
longjmp( state->env, JSONSAX_MISSING_KEY );
name = ++state->json;
for ( ;; )
{
const char* quote = strchr( state->json, '"' );
if ( !quote )
longjmp( state->env, JSONSAX_UNTERMINATED_KEY );
state->json = quote + 1;
if ( quote[ -1 ] != '\\' )
break;
}
HANDLE_2( key, name, state->json - name - 1 );
skip_spaces( state );
if ( *state->json != ':' )
longjmp( state->env, JSONSAX_MISSING_VALUE );
state->json++;
skip_spaces( state );
jsonx_parse_value( state );
skip_spaces( state );
if ( *state->json != ',' )
break;
state->json++;
skip_spaces( state );
}
if ( *state->json != '}' )
longjmp( state->env, JSONSAX_UNTERMINATED_OBJECT );
state->json++;
HANDLE_0( end_object );
}
static void jsonx_parse_array(state_t* state)
{
unsigned int ndx = 0;
state->json++; /* we're sure the current character is a '[' */
skip_spaces( state );
HANDLE_0( start_array );
while ( *state->json != ']' )
{
HANDLE_1( array_index, ndx++ );
jsonx_parse_value( state );
skip_spaces( state );
if ( *state->json != ',' )
break;
state->json++;
skip_spaces( state );
}
if ( *state->json != ']' )
longjmp( state->env, JSONSAX_UNTERMINATED_ARRAY );
state->json++;
HANDLE_0( end_array );
}
static void jsonx_parse_string(state_t* state)
{
const char* string = ++state->json;
for ( ;; )
{
const char* quote = strchr( state->json, '"' );
if ( !quote )
longjmp( state->env, JSONSAX_UNTERMINATED_STRING );
state->json = quote + 1;
if ( quote[ -1 ] != '\\' )
break;
}
HANDLE_2( string, string, state->json - string - 1 );
}
static void jsonx_parse_boolean(state_t* state)
{
if ( !strncmp( state->json, "true", 4 ) )
{
state->json += 4;
HANDLE_1( boolean, 1 );
}
else if ( !strncmp( state->json, "false", 5 ) )
{
state->json += 5;
HANDLE_1( boolean, 0 );
}
else
longjmp( state->env, JSONSAX_INVALID_VALUE );
}
static void jsonx_parse_null(state_t* state)
{
if ( !strncmp( state->json + 1, "ull", 3 ) ) /* we're sure the current character is a 'n' */
{
state->json += 4;
HANDLE_0( null );
}
else
longjmp( state->env, JSONSAX_INVALID_VALUE );
}
static void jsonx_parse_number(state_t* state)
{
const char* number = state->json;
if ( *state->json == '-' )
state->json++;
if ( !isdigit( (unsigned char)*state->json ) )
longjmp( state->env, JSONSAX_INVALID_VALUE );
skip_digits( state );
if ( *state->json == '.' )
{
state->json++;
if ( !isdigit( (unsigned char)*state->json ) )
longjmp( state->env, JSONSAX_INVALID_VALUE );
skip_digits( state );
}
if ( *state->json == 'e' || *state->json == 'E' )
{
state->json++;
if ( *state->json == '-' || *state->json == '+' )
state->json++;
if ( !isdigit( (unsigned char)*state->json ) )
longjmp( state->env, JSONSAX_INVALID_VALUE );
skip_digits( state );
}
HANDLE_2( number, number, state->json - number );
}
static void jsonx_parse_value(state_t* state)
{
skip_spaces( state );
switch ( *state->json )
{
case '{':
jsonx_parse_object(state);
break;
case '[':
jsonx_parse_array( state );
break;
case '"':
jsonx_parse_string( state );
break;
case 't':
case 'f':
jsonx_parse_boolean( state );
break;
case 'n':
jsonx_parse_null( state );
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
jsonx_parse_number( state );
break;
default:
longjmp( state->env, JSONSAX_INVALID_VALUE );
}
}
int jsonsax_parse( const char* json, const jsonsax_handlers_t* handlers, void* userdata )
{
state_t state;
int res;
state.json = json;
state.handlers = handlers;
state.ud = userdata;
if ( ( res = setjmp( state.env ) ) == 0 )
{
if ( handlers->start_document )
handlers->start_document( userdata );
jsonx_parse_value(&state);
if ( handlers->end_document )
handlers->end_document( userdata );
res = JSONSAX_OK;
}
return res;
}

3929
formats/json/jsonsax_full.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
bitstream.c
Helper classes for reading/writing at the bit level.
***************************************************************************/
#include <stdlib.h>
#include <libchdr/bitstream.h>
/***************************************************************************
* INLINE FUNCTIONS
***************************************************************************
*/
int bitstream_overflow(struct bitstream* bitstream) { return ((bitstream->doffset - bitstream->bits / 8) > bitstream->dlength); }
/*-------------------------------------------------
* create_bitstream - constructor
*-------------------------------------------------
*/
struct bitstream* create_bitstream(const void *src, uint32_t srclength)
{
struct bitstream* bitstream = (struct bitstream*)malloc(sizeof(struct bitstream));
bitstream->buffer = 0;
bitstream->bits = 0;
bitstream->read = (const uint8_t*)src;
bitstream->doffset = 0;
bitstream->dlength = srclength;
return bitstream;
}
/*-----------------------------------------------------
* bitstream_peek - fetch the requested number of bits
* but don't advance the input pointer
*-----------------------------------------------------
*/
uint32_t bitstream_peek(struct bitstream* bitstream, int numbits)
{
if (numbits == 0)
return 0;
/* fetch data if we need more */
if (numbits > bitstream->bits)
{
while (bitstream->bits <= 24)
{
if (bitstream->doffset < bitstream->dlength)
bitstream->buffer |= bitstream->read[bitstream->doffset] << (24 - bitstream->bits);
bitstream->doffset++;
bitstream->bits += 8;
}
}
/* return the data */
return bitstream->buffer >> (32 - numbits);
}
/*-----------------------------------------------------
* bitstream_remove - advance the input pointer by the
* specified number of bits
*-----------------------------------------------------
*/
void bitstream_remove(struct bitstream* bitstream, int numbits)
{
bitstream->buffer <<= numbits;
bitstream->bits -= numbits;
}
/*-----------------------------------------------------
* bitstream_read - fetch the requested number of bits
*-----------------------------------------------------
*/
uint32_t bitstream_read(struct bitstream* bitstream, int numbits)
{
uint32_t result = bitstream_peek(bitstream, numbits);
bitstream_remove(bitstream, numbits);
return result;
}
/*-------------------------------------------------
* read_offset - return the current read offset
*-------------------------------------------------
*/
uint32_t bitstream_read_offset(struct bitstream* bitstream)
{
uint32_t result = bitstream->doffset;
int bits = bitstream->bits;
while (bits >= 8)
{
result--;
bits -= 8;
}
return result;
}
/*-------------------------------------------------
* flush - flush to the nearest byte
*-------------------------------------------------
*/
uint32_t bitstream_flush(struct bitstream* bitstream)
{
while (bitstream->bits >= 8)
{
bitstream->doffset--;
bitstream->bits -= 8;
}
bitstream->bits = bitstream->buffer = 0;
return bitstream->doffset;
}

View File

@ -0,0 +1,422 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
cdrom.c
Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this
****************************************************************************
IMPORTANT:
"physical" block addresses are the actual addresses on the emulated CD.
"chd" block addresses are the block addresses in the CHD file.
Because we pad each track to a 4-frame boundary, these addressing
schemes will differ after track 1!
***************************************************************************/
#ifdef WANT_RAW_DATA_SECTOR
#include <assert.h>
#include <string.h>
#include <retro_inline.h>
#include <libchdr/cdrom.h>
/***************************************************************************
DEBUGGING
***************************************************************************/
/** @brief The verbose. */
#define VERBOSE (0)
#if VERBOSE
/**
* @def LOG(x) do
*
* @brief A macro that defines log.
*
* @param x The void to process.
*/
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
/**
* @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
*
* @brief Logerrors the given text.
*
* @param text The text.
*
* @return A CLIB_DECL.
*/
void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
#else
/**
* @def LOG(x);
*
* @brief A macro that defines log.
*
* @param x The void to process.
*/
#define LOG(x)
#endif
/***************************************************************************
CONSTANTS
***************************************************************************/
/** @brief offset within sector. */
#define SYNC_OFFSET 0x000
/** @brief 12 bytes. */
#define SYNC_NUM_BYTES 12
/** @brief offset within sector. */
#define MODE_OFFSET 0x00f
/** @brief offset within sector. */
#define ECC_P_OFFSET 0x81c
/** @brief 2 lots of 86. */
#define ECC_P_NUM_BYTES 86
/** @brief 24 bytes each. */
#define ECC_P_COMP 24
/** @brief The ECC q offset. */
#define ECC_Q_OFFSET (ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES)
/** @brief 2 lots of 52. */
#define ECC_Q_NUM_BYTES 52
/** @brief 43 bytes each. */
#define ECC_Q_COMP 43
/**
* @brief -------------------------------------------------
* ECC lookup tables pre-calculated tables for ECC data calcs
* -------------------------------------------------.
*/
static const uint8_t ecclow[256] =
{
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0x0d, 0x0f, 0x09, 0x0b, 0x05, 0x07, 0x01, 0x03,
0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23,
0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43,
0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63,
0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83,
0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3,
0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3,
0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3
};
/** @brief The ecchigh[ 256]. */
static const uint8_t ecchigh[256] =
{
0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05,
0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe,
0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee,
0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15,
0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce,
0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35,
0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25,
0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde,
0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e,
0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75,
0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65,
0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e,
0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45,
0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe,
0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae,
0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55
};
/**
* @brief -------------------------------------------------
* poffsets - each row represents the addresses used to calculate a byte of the ECC P
* data 86 (*2) ECC P bytes, 24 values represented by each
* -------------------------------------------------.
*/
static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
{
{ 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba },
{ 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb },
{ 0x002,0x058,0x0ae,0x104,0x15a,0x1b0,0x206,0x25c,0x2b2,0x308,0x35e,0x3b4,0x40a,0x460,0x4b6,0x50c,0x562,0x5b8,0x60e,0x664,0x6ba,0x710,0x766,0x7bc },
{ 0x003,0x059,0x0af,0x105,0x15b,0x1b1,0x207,0x25d,0x2b3,0x309,0x35f,0x3b5,0x40b,0x461,0x4b7,0x50d,0x563,0x5b9,0x60f,0x665,0x6bb,0x711,0x767,0x7bd },
{ 0x004,0x05a,0x0b0,0x106,0x15c,0x1b2,0x208,0x25e,0x2b4,0x30a,0x360,0x3b6,0x40c,0x462,0x4b8,0x50e,0x564,0x5ba,0x610,0x666,0x6bc,0x712,0x768,0x7be },
{ 0x005,0x05b,0x0b1,0x107,0x15d,0x1b3,0x209,0x25f,0x2b5,0x30b,0x361,0x3b7,0x40d,0x463,0x4b9,0x50f,0x565,0x5bb,0x611,0x667,0x6bd,0x713,0x769,0x7bf },
{ 0x006,0x05c,0x0b2,0x108,0x15e,0x1b4,0x20a,0x260,0x2b6,0x30c,0x362,0x3b8,0x40e,0x464,0x4ba,0x510,0x566,0x5bc,0x612,0x668,0x6be,0x714,0x76a,0x7c0 },
{ 0x007,0x05d,0x0b3,0x109,0x15f,0x1b5,0x20b,0x261,0x2b7,0x30d,0x363,0x3b9,0x40f,0x465,0x4bb,0x511,0x567,0x5bd,0x613,0x669,0x6bf,0x715,0x76b,0x7c1 },
{ 0x008,0x05e,0x0b4,0x10a,0x160,0x1b6,0x20c,0x262,0x2b8,0x30e,0x364,0x3ba,0x410,0x466,0x4bc,0x512,0x568,0x5be,0x614,0x66a,0x6c0,0x716,0x76c,0x7c2 },
{ 0x009,0x05f,0x0b5,0x10b,0x161,0x1b7,0x20d,0x263,0x2b9,0x30f,0x365,0x3bb,0x411,0x467,0x4bd,0x513,0x569,0x5bf,0x615,0x66b,0x6c1,0x717,0x76d,0x7c3 },
{ 0x00a,0x060,0x0b6,0x10c,0x162,0x1b8,0x20e,0x264,0x2ba,0x310,0x366,0x3bc,0x412,0x468,0x4be,0x514,0x56a,0x5c0,0x616,0x66c,0x6c2,0x718,0x76e,0x7c4 },
{ 0x00b,0x061,0x0b7,0x10d,0x163,0x1b9,0x20f,0x265,0x2bb,0x311,0x367,0x3bd,0x413,0x469,0x4bf,0x515,0x56b,0x5c1,0x617,0x66d,0x6c3,0x719,0x76f,0x7c5 },
{ 0x00c,0x062,0x0b8,0x10e,0x164,0x1ba,0x210,0x266,0x2bc,0x312,0x368,0x3be,0x414,0x46a,0x4c0,0x516,0x56c,0x5c2,0x618,0x66e,0x6c4,0x71a,0x770,0x7c6 },
{ 0x00d,0x063,0x0b9,0x10f,0x165,0x1bb,0x211,0x267,0x2bd,0x313,0x369,0x3bf,0x415,0x46b,0x4c1,0x517,0x56d,0x5c3,0x619,0x66f,0x6c5,0x71b,0x771,0x7c7 },
{ 0x00e,0x064,0x0ba,0x110,0x166,0x1bc,0x212,0x268,0x2be,0x314,0x36a,0x3c0,0x416,0x46c,0x4c2,0x518,0x56e,0x5c4,0x61a,0x670,0x6c6,0x71c,0x772,0x7c8 },
{ 0x00f,0x065,0x0bb,0x111,0x167,0x1bd,0x213,0x269,0x2bf,0x315,0x36b,0x3c1,0x417,0x46d,0x4c3,0x519,0x56f,0x5c5,0x61b,0x671,0x6c7,0x71d,0x773,0x7c9 },
{ 0x010,0x066,0x0bc,0x112,0x168,0x1be,0x214,0x26a,0x2c0,0x316,0x36c,0x3c2,0x418,0x46e,0x4c4,0x51a,0x570,0x5c6,0x61c,0x672,0x6c8,0x71e,0x774,0x7ca },
{ 0x011,0x067,0x0bd,0x113,0x169,0x1bf,0x215,0x26b,0x2c1,0x317,0x36d,0x3c3,0x419,0x46f,0x4c5,0x51b,0x571,0x5c7,0x61d,0x673,0x6c9,0x71f,0x775,0x7cb },
{ 0x012,0x068,0x0be,0x114,0x16a,0x1c0,0x216,0x26c,0x2c2,0x318,0x36e,0x3c4,0x41a,0x470,0x4c6,0x51c,0x572,0x5c8,0x61e,0x674,0x6ca,0x720,0x776,0x7cc },
{ 0x013,0x069,0x0bf,0x115,0x16b,0x1c1,0x217,0x26d,0x2c3,0x319,0x36f,0x3c5,0x41b,0x471,0x4c7,0x51d,0x573,0x5c9,0x61f,0x675,0x6cb,0x721,0x777,0x7cd },
{ 0x014,0x06a,0x0c0,0x116,0x16c,0x1c2,0x218,0x26e,0x2c4,0x31a,0x370,0x3c6,0x41c,0x472,0x4c8,0x51e,0x574,0x5ca,0x620,0x676,0x6cc,0x722,0x778,0x7ce },
{ 0x015,0x06b,0x0c1,0x117,0x16d,0x1c3,0x219,0x26f,0x2c5,0x31b,0x371,0x3c7,0x41d,0x473,0x4c9,0x51f,0x575,0x5cb,0x621,0x677,0x6cd,0x723,0x779,0x7cf },
{ 0x016,0x06c,0x0c2,0x118,0x16e,0x1c4,0x21a,0x270,0x2c6,0x31c,0x372,0x3c8,0x41e,0x474,0x4ca,0x520,0x576,0x5cc,0x622,0x678,0x6ce,0x724,0x77a,0x7d0 },
{ 0x017,0x06d,0x0c3,0x119,0x16f,0x1c5,0x21b,0x271,0x2c7,0x31d,0x373,0x3c9,0x41f,0x475,0x4cb,0x521,0x577,0x5cd,0x623,0x679,0x6cf,0x725,0x77b,0x7d1 },
{ 0x018,0x06e,0x0c4,0x11a,0x170,0x1c6,0x21c,0x272,0x2c8,0x31e,0x374,0x3ca,0x420,0x476,0x4cc,0x522,0x578,0x5ce,0x624,0x67a,0x6d0,0x726,0x77c,0x7d2 },
{ 0x019,0x06f,0x0c5,0x11b,0x171,0x1c7,0x21d,0x273,0x2c9,0x31f,0x375,0x3cb,0x421,0x477,0x4cd,0x523,0x579,0x5cf,0x625,0x67b,0x6d1,0x727,0x77d,0x7d3 },
{ 0x01a,0x070,0x0c6,0x11c,0x172,0x1c8,0x21e,0x274,0x2ca,0x320,0x376,0x3cc,0x422,0x478,0x4ce,0x524,0x57a,0x5d0,0x626,0x67c,0x6d2,0x728,0x77e,0x7d4 },
{ 0x01b,0x071,0x0c7,0x11d,0x173,0x1c9,0x21f,0x275,0x2cb,0x321,0x377,0x3cd,0x423,0x479,0x4cf,0x525,0x57b,0x5d1,0x627,0x67d,0x6d3,0x729,0x77f,0x7d5 },
{ 0x01c,0x072,0x0c8,0x11e,0x174,0x1ca,0x220,0x276,0x2cc,0x322,0x378,0x3ce,0x424,0x47a,0x4d0,0x526,0x57c,0x5d2,0x628,0x67e,0x6d4,0x72a,0x780,0x7d6 },
{ 0x01d,0x073,0x0c9,0x11f,0x175,0x1cb,0x221,0x277,0x2cd,0x323,0x379,0x3cf,0x425,0x47b,0x4d1,0x527,0x57d,0x5d3,0x629,0x67f,0x6d5,0x72b,0x781,0x7d7 },
{ 0x01e,0x074,0x0ca,0x120,0x176,0x1cc,0x222,0x278,0x2ce,0x324,0x37a,0x3d0,0x426,0x47c,0x4d2,0x528,0x57e,0x5d4,0x62a,0x680,0x6d6,0x72c,0x782,0x7d8 },
{ 0x01f,0x075,0x0cb,0x121,0x177,0x1cd,0x223,0x279,0x2cf,0x325,0x37b,0x3d1,0x427,0x47d,0x4d3,0x529,0x57f,0x5d5,0x62b,0x681,0x6d7,0x72d,0x783,0x7d9 },
{ 0x020,0x076,0x0cc,0x122,0x178,0x1ce,0x224,0x27a,0x2d0,0x326,0x37c,0x3d2,0x428,0x47e,0x4d4,0x52a,0x580,0x5d6,0x62c,0x682,0x6d8,0x72e,0x784,0x7da },
{ 0x021,0x077,0x0cd,0x123,0x179,0x1cf,0x225,0x27b,0x2d1,0x327,0x37d,0x3d3,0x429,0x47f,0x4d5,0x52b,0x581,0x5d7,0x62d,0x683,0x6d9,0x72f,0x785,0x7db },
{ 0x022,0x078,0x0ce,0x124,0x17a,0x1d0,0x226,0x27c,0x2d2,0x328,0x37e,0x3d4,0x42a,0x480,0x4d6,0x52c,0x582,0x5d8,0x62e,0x684,0x6da,0x730,0x786,0x7dc },
{ 0x023,0x079,0x0cf,0x125,0x17b,0x1d1,0x227,0x27d,0x2d3,0x329,0x37f,0x3d5,0x42b,0x481,0x4d7,0x52d,0x583,0x5d9,0x62f,0x685,0x6db,0x731,0x787,0x7dd },
{ 0x024,0x07a,0x0d0,0x126,0x17c,0x1d2,0x228,0x27e,0x2d4,0x32a,0x380,0x3d6,0x42c,0x482,0x4d8,0x52e,0x584,0x5da,0x630,0x686,0x6dc,0x732,0x788,0x7de },
{ 0x025,0x07b,0x0d1,0x127,0x17d,0x1d3,0x229,0x27f,0x2d5,0x32b,0x381,0x3d7,0x42d,0x483,0x4d9,0x52f,0x585,0x5db,0x631,0x687,0x6dd,0x733,0x789,0x7df },
{ 0x026,0x07c,0x0d2,0x128,0x17e,0x1d4,0x22a,0x280,0x2d6,0x32c,0x382,0x3d8,0x42e,0x484,0x4da,0x530,0x586,0x5dc,0x632,0x688,0x6de,0x734,0x78a,0x7e0 },
{ 0x027,0x07d,0x0d3,0x129,0x17f,0x1d5,0x22b,0x281,0x2d7,0x32d,0x383,0x3d9,0x42f,0x485,0x4db,0x531,0x587,0x5dd,0x633,0x689,0x6df,0x735,0x78b,0x7e1 },
{ 0x028,0x07e,0x0d4,0x12a,0x180,0x1d6,0x22c,0x282,0x2d8,0x32e,0x384,0x3da,0x430,0x486,0x4dc,0x532,0x588,0x5de,0x634,0x68a,0x6e0,0x736,0x78c,0x7e2 },
{ 0x029,0x07f,0x0d5,0x12b,0x181,0x1d7,0x22d,0x283,0x2d9,0x32f,0x385,0x3db,0x431,0x487,0x4dd,0x533,0x589,0x5df,0x635,0x68b,0x6e1,0x737,0x78d,0x7e3 },
{ 0x02a,0x080,0x0d6,0x12c,0x182,0x1d8,0x22e,0x284,0x2da,0x330,0x386,0x3dc,0x432,0x488,0x4de,0x534,0x58a,0x5e0,0x636,0x68c,0x6e2,0x738,0x78e,0x7e4 },
{ 0x02b,0x081,0x0d7,0x12d,0x183,0x1d9,0x22f,0x285,0x2db,0x331,0x387,0x3dd,0x433,0x489,0x4df,0x535,0x58b,0x5e1,0x637,0x68d,0x6e3,0x739,0x78f,0x7e5 },
{ 0x02c,0x082,0x0d8,0x12e,0x184,0x1da,0x230,0x286,0x2dc,0x332,0x388,0x3de,0x434,0x48a,0x4e0,0x536,0x58c,0x5e2,0x638,0x68e,0x6e4,0x73a,0x790,0x7e6 },
{ 0x02d,0x083,0x0d9,0x12f,0x185,0x1db,0x231,0x287,0x2dd,0x333,0x389,0x3df,0x435,0x48b,0x4e1,0x537,0x58d,0x5e3,0x639,0x68f,0x6e5,0x73b,0x791,0x7e7 },
{ 0x02e,0x084,0x0da,0x130,0x186,0x1dc,0x232,0x288,0x2de,0x334,0x38a,0x3e0,0x436,0x48c,0x4e2,0x538,0x58e,0x5e4,0x63a,0x690,0x6e6,0x73c,0x792,0x7e8 },
{ 0x02f,0x085,0x0db,0x131,0x187,0x1dd,0x233,0x289,0x2df,0x335,0x38b,0x3e1,0x437,0x48d,0x4e3,0x539,0x58f,0x5e5,0x63b,0x691,0x6e7,0x73d,0x793,0x7e9 },
{ 0x030,0x086,0x0dc,0x132,0x188,0x1de,0x234,0x28a,0x2e0,0x336,0x38c,0x3e2,0x438,0x48e,0x4e4,0x53a,0x590,0x5e6,0x63c,0x692,0x6e8,0x73e,0x794,0x7ea },
{ 0x031,0x087,0x0dd,0x133,0x189,0x1df,0x235,0x28b,0x2e1,0x337,0x38d,0x3e3,0x439,0x48f,0x4e5,0x53b,0x591,0x5e7,0x63d,0x693,0x6e9,0x73f,0x795,0x7eb },
{ 0x032,0x088,0x0de,0x134,0x18a,0x1e0,0x236,0x28c,0x2e2,0x338,0x38e,0x3e4,0x43a,0x490,0x4e6,0x53c,0x592,0x5e8,0x63e,0x694,0x6ea,0x740,0x796,0x7ec },
{ 0x033,0x089,0x0df,0x135,0x18b,0x1e1,0x237,0x28d,0x2e3,0x339,0x38f,0x3e5,0x43b,0x491,0x4e7,0x53d,0x593,0x5e9,0x63f,0x695,0x6eb,0x741,0x797,0x7ed },
{ 0x034,0x08a,0x0e0,0x136,0x18c,0x1e2,0x238,0x28e,0x2e4,0x33a,0x390,0x3e6,0x43c,0x492,0x4e8,0x53e,0x594,0x5ea,0x640,0x696,0x6ec,0x742,0x798,0x7ee },
{ 0x035,0x08b,0x0e1,0x137,0x18d,0x1e3,0x239,0x28f,0x2e5,0x33b,0x391,0x3e7,0x43d,0x493,0x4e9,0x53f,0x595,0x5eb,0x641,0x697,0x6ed,0x743,0x799,0x7ef },
{ 0x036,0x08c,0x0e2,0x138,0x18e,0x1e4,0x23a,0x290,0x2e6,0x33c,0x392,0x3e8,0x43e,0x494,0x4ea,0x540,0x596,0x5ec,0x642,0x698,0x6ee,0x744,0x79a,0x7f0 },
{ 0x037,0x08d,0x0e3,0x139,0x18f,0x1e5,0x23b,0x291,0x2e7,0x33d,0x393,0x3e9,0x43f,0x495,0x4eb,0x541,0x597,0x5ed,0x643,0x699,0x6ef,0x745,0x79b,0x7f1 },
{ 0x038,0x08e,0x0e4,0x13a,0x190,0x1e6,0x23c,0x292,0x2e8,0x33e,0x394,0x3ea,0x440,0x496,0x4ec,0x542,0x598,0x5ee,0x644,0x69a,0x6f0,0x746,0x79c,0x7f2 },
{ 0x039,0x08f,0x0e5,0x13b,0x191,0x1e7,0x23d,0x293,0x2e9,0x33f,0x395,0x3eb,0x441,0x497,0x4ed,0x543,0x599,0x5ef,0x645,0x69b,0x6f1,0x747,0x79d,0x7f3 },
{ 0x03a,0x090,0x0e6,0x13c,0x192,0x1e8,0x23e,0x294,0x2ea,0x340,0x396,0x3ec,0x442,0x498,0x4ee,0x544,0x59a,0x5f0,0x646,0x69c,0x6f2,0x748,0x79e,0x7f4 },
{ 0x03b,0x091,0x0e7,0x13d,0x193,0x1e9,0x23f,0x295,0x2eb,0x341,0x397,0x3ed,0x443,0x499,0x4ef,0x545,0x59b,0x5f1,0x647,0x69d,0x6f3,0x749,0x79f,0x7f5 },
{ 0x03c,0x092,0x0e8,0x13e,0x194,0x1ea,0x240,0x296,0x2ec,0x342,0x398,0x3ee,0x444,0x49a,0x4f0,0x546,0x59c,0x5f2,0x648,0x69e,0x6f4,0x74a,0x7a0,0x7f6 },
{ 0x03d,0x093,0x0e9,0x13f,0x195,0x1eb,0x241,0x297,0x2ed,0x343,0x399,0x3ef,0x445,0x49b,0x4f1,0x547,0x59d,0x5f3,0x649,0x69f,0x6f5,0x74b,0x7a1,0x7f7 },
{ 0x03e,0x094,0x0ea,0x140,0x196,0x1ec,0x242,0x298,0x2ee,0x344,0x39a,0x3f0,0x446,0x49c,0x4f2,0x548,0x59e,0x5f4,0x64a,0x6a0,0x6f6,0x74c,0x7a2,0x7f8 },
{ 0x03f,0x095,0x0eb,0x141,0x197,0x1ed,0x243,0x299,0x2ef,0x345,0x39b,0x3f1,0x447,0x49d,0x4f3,0x549,0x59f,0x5f5,0x64b,0x6a1,0x6f7,0x74d,0x7a3,0x7f9 },
{ 0x040,0x096,0x0ec,0x142,0x198,0x1ee,0x244,0x29a,0x2f0,0x346,0x39c,0x3f2,0x448,0x49e,0x4f4,0x54a,0x5a0,0x5f6,0x64c,0x6a2,0x6f8,0x74e,0x7a4,0x7fa },
{ 0x041,0x097,0x0ed,0x143,0x199,0x1ef,0x245,0x29b,0x2f1,0x347,0x39d,0x3f3,0x449,0x49f,0x4f5,0x54b,0x5a1,0x5f7,0x64d,0x6a3,0x6f9,0x74f,0x7a5,0x7fb },
{ 0x042,0x098,0x0ee,0x144,0x19a,0x1f0,0x246,0x29c,0x2f2,0x348,0x39e,0x3f4,0x44a,0x4a0,0x4f6,0x54c,0x5a2,0x5f8,0x64e,0x6a4,0x6fa,0x750,0x7a6,0x7fc },
{ 0x043,0x099,0x0ef,0x145,0x19b,0x1f1,0x247,0x29d,0x2f3,0x349,0x39f,0x3f5,0x44b,0x4a1,0x4f7,0x54d,0x5a3,0x5f9,0x64f,0x6a5,0x6fb,0x751,0x7a7,0x7fd },
{ 0x044,0x09a,0x0f0,0x146,0x19c,0x1f2,0x248,0x29e,0x2f4,0x34a,0x3a0,0x3f6,0x44c,0x4a2,0x4f8,0x54e,0x5a4,0x5fa,0x650,0x6a6,0x6fc,0x752,0x7a8,0x7fe },
{ 0x045,0x09b,0x0f1,0x147,0x19d,0x1f3,0x249,0x29f,0x2f5,0x34b,0x3a1,0x3f7,0x44d,0x4a3,0x4f9,0x54f,0x5a5,0x5fb,0x651,0x6a7,0x6fd,0x753,0x7a9,0x7ff },
{ 0x046,0x09c,0x0f2,0x148,0x19e,0x1f4,0x24a,0x2a0,0x2f6,0x34c,0x3a2,0x3f8,0x44e,0x4a4,0x4fa,0x550,0x5a6,0x5fc,0x652,0x6a8,0x6fe,0x754,0x7aa,0x800 },
{ 0x047,0x09d,0x0f3,0x149,0x19f,0x1f5,0x24b,0x2a1,0x2f7,0x34d,0x3a3,0x3f9,0x44f,0x4a5,0x4fb,0x551,0x5a7,0x5fd,0x653,0x6a9,0x6ff,0x755,0x7ab,0x801 },
{ 0x048,0x09e,0x0f4,0x14a,0x1a0,0x1f6,0x24c,0x2a2,0x2f8,0x34e,0x3a4,0x3fa,0x450,0x4a6,0x4fc,0x552,0x5a8,0x5fe,0x654,0x6aa,0x700,0x756,0x7ac,0x802 },
{ 0x049,0x09f,0x0f5,0x14b,0x1a1,0x1f7,0x24d,0x2a3,0x2f9,0x34f,0x3a5,0x3fb,0x451,0x4a7,0x4fd,0x553,0x5a9,0x5ff,0x655,0x6ab,0x701,0x757,0x7ad,0x803 },
{ 0x04a,0x0a0,0x0f6,0x14c,0x1a2,0x1f8,0x24e,0x2a4,0x2fa,0x350,0x3a6,0x3fc,0x452,0x4a8,0x4fe,0x554,0x5aa,0x600,0x656,0x6ac,0x702,0x758,0x7ae,0x804 },
{ 0x04b,0x0a1,0x0f7,0x14d,0x1a3,0x1f9,0x24f,0x2a5,0x2fb,0x351,0x3a7,0x3fd,0x453,0x4a9,0x4ff,0x555,0x5ab,0x601,0x657,0x6ad,0x703,0x759,0x7af,0x805 },
{ 0x04c,0x0a2,0x0f8,0x14e,0x1a4,0x1fa,0x250,0x2a6,0x2fc,0x352,0x3a8,0x3fe,0x454,0x4aa,0x500,0x556,0x5ac,0x602,0x658,0x6ae,0x704,0x75a,0x7b0,0x806 },
{ 0x04d,0x0a3,0x0f9,0x14f,0x1a5,0x1fb,0x251,0x2a7,0x2fd,0x353,0x3a9,0x3ff,0x455,0x4ab,0x501,0x557,0x5ad,0x603,0x659,0x6af,0x705,0x75b,0x7b1,0x807 },
{ 0x04e,0x0a4,0x0fa,0x150,0x1a6,0x1fc,0x252,0x2a8,0x2fe,0x354,0x3aa,0x400,0x456,0x4ac,0x502,0x558,0x5ae,0x604,0x65a,0x6b0,0x706,0x75c,0x7b2,0x808 },
{ 0x04f,0x0a5,0x0fb,0x151,0x1a7,0x1fd,0x253,0x2a9,0x2ff,0x355,0x3ab,0x401,0x457,0x4ad,0x503,0x559,0x5af,0x605,0x65b,0x6b1,0x707,0x75d,0x7b3,0x809 },
{ 0x050,0x0a6,0x0fc,0x152,0x1a8,0x1fe,0x254,0x2aa,0x300,0x356,0x3ac,0x402,0x458,0x4ae,0x504,0x55a,0x5b0,0x606,0x65c,0x6b2,0x708,0x75e,0x7b4,0x80a },
{ 0x051,0x0a7,0x0fd,0x153,0x1a9,0x1ff,0x255,0x2ab,0x301,0x357,0x3ad,0x403,0x459,0x4af,0x505,0x55b,0x5b1,0x607,0x65d,0x6b3,0x709,0x75f,0x7b5,0x80b },
{ 0x052,0x0a8,0x0fe,0x154,0x1aa,0x200,0x256,0x2ac,0x302,0x358,0x3ae,0x404,0x45a,0x4b0,0x506,0x55c,0x5b2,0x608,0x65e,0x6b4,0x70a,0x760,0x7b6,0x80c },
{ 0x053,0x0a9,0x0ff,0x155,0x1ab,0x201,0x257,0x2ad,0x303,0x359,0x3af,0x405,0x45b,0x4b1,0x507,0x55d,0x5b3,0x609,0x65f,0x6b5,0x70b,0x761,0x7b7,0x80d },
{ 0x054,0x0aa,0x100,0x156,0x1ac,0x202,0x258,0x2ae,0x304,0x35a,0x3b0,0x406,0x45c,0x4b2,0x508,0x55e,0x5b4,0x60a,0x660,0x6b6,0x70c,0x762,0x7b8,0x80e },
{ 0x055,0x0ab,0x101,0x157,0x1ad,0x203,0x259,0x2af,0x305,0x35b,0x3b1,0x407,0x45d,0x4b3,0x509,0x55f,0x5b5,0x60b,0x661,0x6b7,0x70d,0x763,0x7b9,0x80f }
};
/**
* @brief -------------------------------------------------
* qoffsets - each row represents the addresses used to calculate a byte of the ECC Q
* data 52 (*2) ECC Q bytes, 43 values represented by each
* -------------------------------------------------.
*/
static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
{
{ 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 },
{ 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 },
{ 0x056,0x0ae,0x106,0x15e,0x1b6,0x20e,0x266,0x2be,0x316,0x36e,0x3c6,0x41e,0x476,0x4ce,0x526,0x57e,0x5d6,0x62e,0x686,0x6de,0x736,0x78e,0x7e6,0x83e,0x896,0x032,0x08a,0x0e2,0x13a,0x192,0x1ea,0x242,0x29a,0x2f2,0x34a,0x3a2,0x3fa,0x452,0x4aa,0x502,0x55a,0x5b2,0x60a },
{ 0x057,0x0af,0x107,0x15f,0x1b7,0x20f,0x267,0x2bf,0x317,0x36f,0x3c7,0x41f,0x477,0x4cf,0x527,0x57f,0x5d7,0x62f,0x687,0x6df,0x737,0x78f,0x7e7,0x83f,0x897,0x033,0x08b,0x0e3,0x13b,0x193,0x1eb,0x243,0x29b,0x2f3,0x34b,0x3a3,0x3fb,0x453,0x4ab,0x503,0x55b,0x5b3,0x60b },
{ 0x0ac,0x104,0x15c,0x1b4,0x20c,0x264,0x2bc,0x314,0x36c,0x3c4,0x41c,0x474,0x4cc,0x524,0x57c,0x5d4,0x62c,0x684,0x6dc,0x734,0x78c,0x7e4,0x83c,0x894,0x030,0x088,0x0e0,0x138,0x190,0x1e8,0x240,0x298,0x2f0,0x348,0x3a0,0x3f8,0x450,0x4a8,0x500,0x558,0x5b0,0x608,0x660 },
{ 0x0ad,0x105,0x15d,0x1b5,0x20d,0x265,0x2bd,0x315,0x36d,0x3c5,0x41d,0x475,0x4cd,0x525,0x57d,0x5d5,0x62d,0x685,0x6dd,0x735,0x78d,0x7e5,0x83d,0x895,0x031,0x089,0x0e1,0x139,0x191,0x1e9,0x241,0x299,0x2f1,0x349,0x3a1,0x3f9,0x451,0x4a9,0x501,0x559,0x5b1,0x609,0x661 },
{ 0x102,0x15a,0x1b2,0x20a,0x262,0x2ba,0x312,0x36a,0x3c2,0x41a,0x472,0x4ca,0x522,0x57a,0x5d2,0x62a,0x682,0x6da,0x732,0x78a,0x7e2,0x83a,0x892,0x02e,0x086,0x0de,0x136,0x18e,0x1e6,0x23e,0x296,0x2ee,0x346,0x39e,0x3f6,0x44e,0x4a6,0x4fe,0x556,0x5ae,0x606,0x65e,0x6b6 },
{ 0x103,0x15b,0x1b3,0x20b,0x263,0x2bb,0x313,0x36b,0x3c3,0x41b,0x473,0x4cb,0x523,0x57b,0x5d3,0x62b,0x683,0x6db,0x733,0x78b,0x7e3,0x83b,0x893,0x02f,0x087,0x0df,0x137,0x18f,0x1e7,0x23f,0x297,0x2ef,0x347,0x39f,0x3f7,0x44f,0x4a7,0x4ff,0x557,0x5af,0x607,0x65f,0x6b7 },
{ 0x158,0x1b0,0x208,0x260,0x2b8,0x310,0x368,0x3c0,0x418,0x470,0x4c8,0x520,0x578,0x5d0,0x628,0x680,0x6d8,0x730,0x788,0x7e0,0x838,0x890,0x02c,0x084,0x0dc,0x134,0x18c,0x1e4,0x23c,0x294,0x2ec,0x344,0x39c,0x3f4,0x44c,0x4a4,0x4fc,0x554,0x5ac,0x604,0x65c,0x6b4,0x70c },
{ 0x159,0x1b1,0x209,0x261,0x2b9,0x311,0x369,0x3c1,0x419,0x471,0x4c9,0x521,0x579,0x5d1,0x629,0x681,0x6d9,0x731,0x789,0x7e1,0x839,0x891,0x02d,0x085,0x0dd,0x135,0x18d,0x1e5,0x23d,0x295,0x2ed,0x345,0x39d,0x3f5,0x44d,0x4a5,0x4fd,0x555,0x5ad,0x605,0x65d,0x6b5,0x70d },
{ 0x1ae,0x206,0x25e,0x2b6,0x30e,0x366,0x3be,0x416,0x46e,0x4c6,0x51e,0x576,0x5ce,0x626,0x67e,0x6d6,0x72e,0x786,0x7de,0x836,0x88e,0x02a,0x082,0x0da,0x132,0x18a,0x1e2,0x23a,0x292,0x2ea,0x342,0x39a,0x3f2,0x44a,0x4a2,0x4fa,0x552,0x5aa,0x602,0x65a,0x6b2,0x70a,0x762 },
{ 0x1af,0x207,0x25f,0x2b7,0x30f,0x367,0x3bf,0x417,0x46f,0x4c7,0x51f,0x577,0x5cf,0x627,0x67f,0x6d7,0x72f,0x787,0x7df,0x837,0x88f,0x02b,0x083,0x0db,0x133,0x18b,0x1e3,0x23b,0x293,0x2eb,0x343,0x39b,0x3f3,0x44b,0x4a3,0x4fb,0x553,0x5ab,0x603,0x65b,0x6b3,0x70b,0x763 },
{ 0x204,0x25c,0x2b4,0x30c,0x364,0x3bc,0x414,0x46c,0x4c4,0x51c,0x574,0x5cc,0x624,0x67c,0x6d4,0x72c,0x784,0x7dc,0x834,0x88c,0x028,0x080,0x0d8,0x130,0x188,0x1e0,0x238,0x290,0x2e8,0x340,0x398,0x3f0,0x448,0x4a0,0x4f8,0x550,0x5a8,0x600,0x658,0x6b0,0x708,0x760,0x7b8 },
{ 0x205,0x25d,0x2b5,0x30d,0x365,0x3bd,0x415,0x46d,0x4c5,0x51d,0x575,0x5cd,0x625,0x67d,0x6d5,0x72d,0x785,0x7dd,0x835,0x88d,0x029,0x081,0x0d9,0x131,0x189,0x1e1,0x239,0x291,0x2e9,0x341,0x399,0x3f1,0x449,0x4a1,0x4f9,0x551,0x5a9,0x601,0x659,0x6b1,0x709,0x761,0x7b9 },
{ 0x25a,0x2b2,0x30a,0x362,0x3ba,0x412,0x46a,0x4c2,0x51a,0x572,0x5ca,0x622,0x67a,0x6d2,0x72a,0x782,0x7da,0x832,0x88a,0x026,0x07e,0x0d6,0x12e,0x186,0x1de,0x236,0x28e,0x2e6,0x33e,0x396,0x3ee,0x446,0x49e,0x4f6,0x54e,0x5a6,0x5fe,0x656,0x6ae,0x706,0x75e,0x7b6,0x80e },
{ 0x25b,0x2b3,0x30b,0x363,0x3bb,0x413,0x46b,0x4c3,0x51b,0x573,0x5cb,0x623,0x67b,0x6d3,0x72b,0x783,0x7db,0x833,0x88b,0x027,0x07f,0x0d7,0x12f,0x187,0x1df,0x237,0x28f,0x2e7,0x33f,0x397,0x3ef,0x447,0x49f,0x4f7,0x54f,0x5a7,0x5ff,0x657,0x6af,0x707,0x75f,0x7b7,0x80f },
{ 0x2b0,0x308,0x360,0x3b8,0x410,0x468,0x4c0,0x518,0x570,0x5c8,0x620,0x678,0x6d0,0x728,0x780,0x7d8,0x830,0x888,0x024,0x07c,0x0d4,0x12c,0x184,0x1dc,0x234,0x28c,0x2e4,0x33c,0x394,0x3ec,0x444,0x49c,0x4f4,0x54c,0x5a4,0x5fc,0x654,0x6ac,0x704,0x75c,0x7b4,0x80c,0x864 },
{ 0x2b1,0x309,0x361,0x3b9,0x411,0x469,0x4c1,0x519,0x571,0x5c9,0x621,0x679,0x6d1,0x729,0x781,0x7d9,0x831,0x889,0x025,0x07d,0x0d5,0x12d,0x185,0x1dd,0x235,0x28d,0x2e5,0x33d,0x395,0x3ed,0x445,0x49d,0x4f5,0x54d,0x5a5,0x5fd,0x655,0x6ad,0x705,0x75d,0x7b5,0x80d,0x865 },
{ 0x306,0x35e,0x3b6,0x40e,0x466,0x4be,0x516,0x56e,0x5c6,0x61e,0x676,0x6ce,0x726,0x77e,0x7d6,0x82e,0x886,0x022,0x07a,0x0d2,0x12a,0x182,0x1da,0x232,0x28a,0x2e2,0x33a,0x392,0x3ea,0x442,0x49a,0x4f2,0x54a,0x5a2,0x5fa,0x652,0x6aa,0x702,0x75a,0x7b2,0x80a,0x862,0x8ba },
{ 0x307,0x35f,0x3b7,0x40f,0x467,0x4bf,0x517,0x56f,0x5c7,0x61f,0x677,0x6cf,0x727,0x77f,0x7d7,0x82f,0x887,0x023,0x07b,0x0d3,0x12b,0x183,0x1db,0x233,0x28b,0x2e3,0x33b,0x393,0x3eb,0x443,0x49b,0x4f3,0x54b,0x5a3,0x5fb,0x653,0x6ab,0x703,0x75b,0x7b3,0x80b,0x863,0x8bb },
{ 0x35c,0x3b4,0x40c,0x464,0x4bc,0x514,0x56c,0x5c4,0x61c,0x674,0x6cc,0x724,0x77c,0x7d4,0x82c,0x884,0x020,0x078,0x0d0,0x128,0x180,0x1d8,0x230,0x288,0x2e0,0x338,0x390,0x3e8,0x440,0x498,0x4f0,0x548,0x5a0,0x5f8,0x650,0x6a8,0x700,0x758,0x7b0,0x808,0x860,0x8b8,0x054 },
{ 0x35d,0x3b5,0x40d,0x465,0x4bd,0x515,0x56d,0x5c5,0x61d,0x675,0x6cd,0x725,0x77d,0x7d5,0x82d,0x885,0x021,0x079,0x0d1,0x129,0x181,0x1d9,0x231,0x289,0x2e1,0x339,0x391,0x3e9,0x441,0x499,0x4f1,0x549,0x5a1,0x5f9,0x651,0x6a9,0x701,0x759,0x7b1,0x809,0x861,0x8b9,0x055 },
{ 0x3b2,0x40a,0x462,0x4ba,0x512,0x56a,0x5c2,0x61a,0x672,0x6ca,0x722,0x77a,0x7d2,0x82a,0x882,0x01e,0x076,0x0ce,0x126,0x17e,0x1d6,0x22e,0x286,0x2de,0x336,0x38e,0x3e6,0x43e,0x496,0x4ee,0x546,0x59e,0x5f6,0x64e,0x6a6,0x6fe,0x756,0x7ae,0x806,0x85e,0x8b6,0x052,0x0aa },
{ 0x3b3,0x40b,0x463,0x4bb,0x513,0x56b,0x5c3,0x61b,0x673,0x6cb,0x723,0x77b,0x7d3,0x82b,0x883,0x01f,0x077,0x0cf,0x127,0x17f,0x1d7,0x22f,0x287,0x2df,0x337,0x38f,0x3e7,0x43f,0x497,0x4ef,0x547,0x59f,0x5f7,0x64f,0x6a7,0x6ff,0x757,0x7af,0x807,0x85f,0x8b7,0x053,0x0ab },
{ 0x408,0x460,0x4b8,0x510,0x568,0x5c0,0x618,0x670,0x6c8,0x720,0x778,0x7d0,0x828,0x880,0x01c,0x074,0x0cc,0x124,0x17c,0x1d4,0x22c,0x284,0x2dc,0x334,0x38c,0x3e4,0x43c,0x494,0x4ec,0x544,0x59c,0x5f4,0x64c,0x6a4,0x6fc,0x754,0x7ac,0x804,0x85c,0x8b4,0x050,0x0a8,0x100 },
{ 0x409,0x461,0x4b9,0x511,0x569,0x5c1,0x619,0x671,0x6c9,0x721,0x779,0x7d1,0x829,0x881,0x01d,0x075,0x0cd,0x125,0x17d,0x1d5,0x22d,0x285,0x2dd,0x335,0x38d,0x3e5,0x43d,0x495,0x4ed,0x545,0x59d,0x5f5,0x64d,0x6a5,0x6fd,0x755,0x7ad,0x805,0x85d,0x8b5,0x051,0x0a9,0x101 },
{ 0x45e,0x4b6,0x50e,0x566,0x5be,0x616,0x66e,0x6c6,0x71e,0x776,0x7ce,0x826,0x87e,0x01a,0x072,0x0ca,0x122,0x17a,0x1d2,0x22a,0x282,0x2da,0x332,0x38a,0x3e2,0x43a,0x492,0x4ea,0x542,0x59a,0x5f2,0x64a,0x6a2,0x6fa,0x752,0x7aa,0x802,0x85a,0x8b2,0x04e,0x0a6,0x0fe,0x156 },
{ 0x45f,0x4b7,0x50f,0x567,0x5bf,0x617,0x66f,0x6c7,0x71f,0x777,0x7cf,0x827,0x87f,0x01b,0x073,0x0cb,0x123,0x17b,0x1d3,0x22b,0x283,0x2db,0x333,0x38b,0x3e3,0x43b,0x493,0x4eb,0x543,0x59b,0x5f3,0x64b,0x6a3,0x6fb,0x753,0x7ab,0x803,0x85b,0x8b3,0x04f,0x0a7,0x0ff,0x157 },
{ 0x4b4,0x50c,0x564,0x5bc,0x614,0x66c,0x6c4,0x71c,0x774,0x7cc,0x824,0x87c,0x018,0x070,0x0c8,0x120,0x178,0x1d0,0x228,0x280,0x2d8,0x330,0x388,0x3e0,0x438,0x490,0x4e8,0x540,0x598,0x5f0,0x648,0x6a0,0x6f8,0x750,0x7a8,0x800,0x858,0x8b0,0x04c,0x0a4,0x0fc,0x154,0x1ac },
{ 0x4b5,0x50d,0x565,0x5bd,0x615,0x66d,0x6c5,0x71d,0x775,0x7cd,0x825,0x87d,0x019,0x071,0x0c9,0x121,0x179,0x1d1,0x229,0x281,0x2d9,0x331,0x389,0x3e1,0x439,0x491,0x4e9,0x541,0x599,0x5f1,0x649,0x6a1,0x6f9,0x751,0x7a9,0x801,0x859,0x8b1,0x04d,0x0a5,0x0fd,0x155,0x1ad },
{ 0x50a,0x562,0x5ba,0x612,0x66a,0x6c2,0x71a,0x772,0x7ca,0x822,0x87a,0x016,0x06e,0x0c6,0x11e,0x176,0x1ce,0x226,0x27e,0x2d6,0x32e,0x386,0x3de,0x436,0x48e,0x4e6,0x53e,0x596,0x5ee,0x646,0x69e,0x6f6,0x74e,0x7a6,0x7fe,0x856,0x8ae,0x04a,0x0a2,0x0fa,0x152,0x1aa,0x202 },
{ 0x50b,0x563,0x5bb,0x613,0x66b,0x6c3,0x71b,0x773,0x7cb,0x823,0x87b,0x017,0x06f,0x0c7,0x11f,0x177,0x1cf,0x227,0x27f,0x2d7,0x32f,0x387,0x3df,0x437,0x48f,0x4e7,0x53f,0x597,0x5ef,0x647,0x69f,0x6f7,0x74f,0x7a7,0x7ff,0x857,0x8af,0x04b,0x0a3,0x0fb,0x153,0x1ab,0x203 },
{ 0x560,0x5b8,0x610,0x668,0x6c0,0x718,0x770,0x7c8,0x820,0x878,0x014,0x06c,0x0c4,0x11c,0x174,0x1cc,0x224,0x27c,0x2d4,0x32c,0x384,0x3dc,0x434,0x48c,0x4e4,0x53c,0x594,0x5ec,0x644,0x69c,0x6f4,0x74c,0x7a4,0x7fc,0x854,0x8ac,0x048,0x0a0,0x0f8,0x150,0x1a8,0x200,0x258 },
{ 0x561,0x5b9,0x611,0x669,0x6c1,0x719,0x771,0x7c9,0x821,0x879,0x015,0x06d,0x0c5,0x11d,0x175,0x1cd,0x225,0x27d,0x2d5,0x32d,0x385,0x3dd,0x435,0x48d,0x4e5,0x53d,0x595,0x5ed,0x645,0x69d,0x6f5,0x74d,0x7a5,0x7fd,0x855,0x8ad,0x049,0x0a1,0x0f9,0x151,0x1a9,0x201,0x259 },
{ 0x5b6,0x60e,0x666,0x6be,0x716,0x76e,0x7c6,0x81e,0x876,0x012,0x06a,0x0c2,0x11a,0x172,0x1ca,0x222,0x27a,0x2d2,0x32a,0x382,0x3da,0x432,0x48a,0x4e2,0x53a,0x592,0x5ea,0x642,0x69a,0x6f2,0x74a,0x7a2,0x7fa,0x852,0x8aa,0x046,0x09e,0x0f6,0x14e,0x1a6,0x1fe,0x256,0x2ae },
{ 0x5b7,0x60f,0x667,0x6bf,0x717,0x76f,0x7c7,0x81f,0x877,0x013,0x06b,0x0c3,0x11b,0x173,0x1cb,0x223,0x27b,0x2d3,0x32b,0x383,0x3db,0x433,0x48b,0x4e3,0x53b,0x593,0x5eb,0x643,0x69b,0x6f3,0x74b,0x7a3,0x7fb,0x853,0x8ab,0x047,0x09f,0x0f7,0x14f,0x1a7,0x1ff,0x257,0x2af },
{ 0x60c,0x664,0x6bc,0x714,0x76c,0x7c4,0x81c,0x874,0x010,0x068,0x0c0,0x118,0x170,0x1c8,0x220,0x278,0x2d0,0x328,0x380,0x3d8,0x430,0x488,0x4e0,0x538,0x590,0x5e8,0x640,0x698,0x6f0,0x748,0x7a0,0x7f8,0x850,0x8a8,0x044,0x09c,0x0f4,0x14c,0x1a4,0x1fc,0x254,0x2ac,0x304 },
{ 0x60d,0x665,0x6bd,0x715,0x76d,0x7c5,0x81d,0x875,0x011,0x069,0x0c1,0x119,0x171,0x1c9,0x221,0x279,0x2d1,0x329,0x381,0x3d9,0x431,0x489,0x4e1,0x539,0x591,0x5e9,0x641,0x699,0x6f1,0x749,0x7a1,0x7f9,0x851,0x8a9,0x045,0x09d,0x0f5,0x14d,0x1a5,0x1fd,0x255,0x2ad,0x305 },
{ 0x662,0x6ba,0x712,0x76a,0x7c2,0x81a,0x872,0x00e,0x066,0x0be,0x116,0x16e,0x1c6,0x21e,0x276,0x2ce,0x326,0x37e,0x3d6,0x42e,0x486,0x4de,0x536,0x58e,0x5e6,0x63e,0x696,0x6ee,0x746,0x79e,0x7f6,0x84e,0x8a6,0x042,0x09a,0x0f2,0x14a,0x1a2,0x1fa,0x252,0x2aa,0x302,0x35a },
{ 0x663,0x6bb,0x713,0x76b,0x7c3,0x81b,0x873,0x00f,0x067,0x0bf,0x117,0x16f,0x1c7,0x21f,0x277,0x2cf,0x327,0x37f,0x3d7,0x42f,0x487,0x4df,0x537,0x58f,0x5e7,0x63f,0x697,0x6ef,0x747,0x79f,0x7f7,0x84f,0x8a7,0x043,0x09b,0x0f3,0x14b,0x1a3,0x1fb,0x253,0x2ab,0x303,0x35b },
{ 0x6b8,0x710,0x768,0x7c0,0x818,0x870,0x00c,0x064,0x0bc,0x114,0x16c,0x1c4,0x21c,0x274,0x2cc,0x324,0x37c,0x3d4,0x42c,0x484,0x4dc,0x534,0x58c,0x5e4,0x63c,0x694,0x6ec,0x744,0x79c,0x7f4,0x84c,0x8a4,0x040,0x098,0x0f0,0x148,0x1a0,0x1f8,0x250,0x2a8,0x300,0x358,0x3b0 },
{ 0x6b9,0x711,0x769,0x7c1,0x819,0x871,0x00d,0x065,0x0bd,0x115,0x16d,0x1c5,0x21d,0x275,0x2cd,0x325,0x37d,0x3d5,0x42d,0x485,0x4dd,0x535,0x58d,0x5e5,0x63d,0x695,0x6ed,0x745,0x79d,0x7f5,0x84d,0x8a5,0x041,0x099,0x0f1,0x149,0x1a1,0x1f9,0x251,0x2a9,0x301,0x359,0x3b1 },
{ 0x70e,0x766,0x7be,0x816,0x86e,0x00a,0x062,0x0ba,0x112,0x16a,0x1c2,0x21a,0x272,0x2ca,0x322,0x37a,0x3d2,0x42a,0x482,0x4da,0x532,0x58a,0x5e2,0x63a,0x692,0x6ea,0x742,0x79a,0x7f2,0x84a,0x8a2,0x03e,0x096,0x0ee,0x146,0x19e,0x1f6,0x24e,0x2a6,0x2fe,0x356,0x3ae,0x406 },
{ 0x70f,0x767,0x7bf,0x817,0x86f,0x00b,0x063,0x0bb,0x113,0x16b,0x1c3,0x21b,0x273,0x2cb,0x323,0x37b,0x3d3,0x42b,0x483,0x4db,0x533,0x58b,0x5e3,0x63b,0x693,0x6eb,0x743,0x79b,0x7f3,0x84b,0x8a3,0x03f,0x097,0x0ef,0x147,0x19f,0x1f7,0x24f,0x2a7,0x2ff,0x357,0x3af,0x407 },
{ 0x764,0x7bc,0x814,0x86c,0x008,0x060,0x0b8,0x110,0x168,0x1c0,0x218,0x270,0x2c8,0x320,0x378,0x3d0,0x428,0x480,0x4d8,0x530,0x588,0x5e0,0x638,0x690,0x6e8,0x740,0x798,0x7f0,0x848,0x8a0,0x03c,0x094,0x0ec,0x144,0x19c,0x1f4,0x24c,0x2a4,0x2fc,0x354,0x3ac,0x404,0x45c },
{ 0x765,0x7bd,0x815,0x86d,0x009,0x061,0x0b9,0x111,0x169,0x1c1,0x219,0x271,0x2c9,0x321,0x379,0x3d1,0x429,0x481,0x4d9,0x531,0x589,0x5e1,0x639,0x691,0x6e9,0x741,0x799,0x7f1,0x849,0x8a1,0x03d,0x095,0x0ed,0x145,0x19d,0x1f5,0x24d,0x2a5,0x2fd,0x355,0x3ad,0x405,0x45d },
{ 0x7ba,0x812,0x86a,0x006,0x05e,0x0b6,0x10e,0x166,0x1be,0x216,0x26e,0x2c6,0x31e,0x376,0x3ce,0x426,0x47e,0x4d6,0x52e,0x586,0x5de,0x636,0x68e,0x6e6,0x73e,0x796,0x7ee,0x846,0x89e,0x03a,0x092,0x0ea,0x142,0x19a,0x1f2,0x24a,0x2a2,0x2fa,0x352,0x3aa,0x402,0x45a,0x4b2 },
{ 0x7bb,0x813,0x86b,0x007,0x05f,0x0b7,0x10f,0x167,0x1bf,0x217,0x26f,0x2c7,0x31f,0x377,0x3cf,0x427,0x47f,0x4d7,0x52f,0x587,0x5df,0x637,0x68f,0x6e7,0x73f,0x797,0x7ef,0x847,0x89f,0x03b,0x093,0x0eb,0x143,0x19b,0x1f3,0x24b,0x2a3,0x2fb,0x353,0x3ab,0x403,0x45b,0x4b3 },
{ 0x810,0x868,0x004,0x05c,0x0b4,0x10c,0x164,0x1bc,0x214,0x26c,0x2c4,0x31c,0x374,0x3cc,0x424,0x47c,0x4d4,0x52c,0x584,0x5dc,0x634,0x68c,0x6e4,0x73c,0x794,0x7ec,0x844,0x89c,0x038,0x090,0x0e8,0x140,0x198,0x1f0,0x248,0x2a0,0x2f8,0x350,0x3a8,0x400,0x458,0x4b0,0x508 },
{ 0x811,0x869,0x005,0x05d,0x0b5,0x10d,0x165,0x1bd,0x215,0x26d,0x2c5,0x31d,0x375,0x3cd,0x425,0x47d,0x4d5,0x52d,0x585,0x5dd,0x635,0x68d,0x6e5,0x73d,0x795,0x7ed,0x845,0x89d,0x039,0x091,0x0e9,0x141,0x199,0x1f1,0x249,0x2a1,0x2f9,0x351,0x3a9,0x401,0x459,0x4b1,0x509 },
{ 0x866,0x002,0x05a,0x0b2,0x10a,0x162,0x1ba,0x212,0x26a,0x2c2,0x31a,0x372,0x3ca,0x422,0x47a,0x4d2,0x52a,0x582,0x5da,0x632,0x68a,0x6e2,0x73a,0x792,0x7ea,0x842,0x89a,0x036,0x08e,0x0e6,0x13e,0x196,0x1ee,0x246,0x29e,0x2f6,0x34e,0x3a6,0x3fe,0x456,0x4ae,0x506,0x55e },
{ 0x867,0x003,0x05b,0x0b3,0x10b,0x163,0x1bb,0x213,0x26b,0x2c3,0x31b,0x373,0x3cb,0x423,0x47b,0x4d3,0x52b,0x583,0x5db,0x633,0x68b,0x6e3,0x73b,0x793,0x7eb,0x843,0x89b,0x037,0x08f,0x0e7,0x13f,0x197,0x1ef,0x247,0x29f,0x2f7,0x34f,0x3a7,0x3ff,0x457,0x4af,0x507,0x55f }
};
/*-------------------------------------------------
* ecc_source_byte - return data from the sector
* at the given offset, masking anything
* particular to a mode
*-------------------------------------------------
*/
static INLINE uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
{
/* in mode 2 always treat these as 0 bytes */
return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset];
}
/**
* @fn void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
*
* @brief -------------------------------------------------
* ecc_compute_bytes - calculate an ECC value (P or Q)
* -------------------------------------------------.
*
* @param sector The sector.
* @param row The row.
* @param rowlen The rowlen.
* @param [in,out] val1 The first value.
* @param [in,out] val2 The second value.
*/
void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t *val1, uint8_t *val2)
{
int component;
*val1 = *val2 = 0;
for (component = 0; component < rowlen; component++)
{
*val1 ^= ecc_source_byte(sector, row[component]);
*val2 ^= ecc_source_byte(sector, row[component]);
*val1 = ecclow[*val1];
}
*val1 = ecchigh[ecclow[*val1] ^ *val2];
*val2 ^= *val1;
}
/**
* @fn int ecc_verify(const uint8_t *sector)
*
* @brief -------------------------------------------------
* ecc_verify - verify the P and Q ECC codes in a sector
* -------------------------------------------------.
*
* @param sector The sector.
*
* @return true if it succeeds, false if it fails.
*/
int ecc_verify(const uint8_t *sector)
{
int byte;
/* first verify P bytes */
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
{
uint8_t val1, val2;
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &val1, &val2);
if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2)
return 0;
}
/* then verify Q bytes */
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
{
uint8_t val1, val2;
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &val1, &val2);
if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2)
return 0;
}
return 1;
}
/**
* @fn void ecc_generate(uint8_t *sector)
*
* @brief -------------------------------------------------
* ecc_generate - generate the P and Q ECC codes for a sector, overwriting any
* existing codes
* -------------------------------------------------.
*
* @param [in,out] sector If non-null, the sector.
*/
void ecc_generate(uint8_t *sector)
{
int byte;
/* first verify P bytes */
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &sector[ECC_P_OFFSET + byte], &sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]);
/* then verify Q bytes */
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &sector[ECC_Q_OFFSET + byte], &sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]);
}
/**
* @fn void ecc_clear(uint8_t *sector)
*
* @brief -------------------------------------------------
* ecc_clear - erase the ECC P and Q cods to 0 within a sector
* -------------------------------------------------.
*
* @param [in,out] sector If non-null, the sector.
*/
void ecc_clear(uint8_t *sector)
{
memset(&sector[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
memset(&sector[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
}
#endif /* WANT_RAW_DATA_SECTOR */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,323 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
flac.c
FLAC compression wrappers
***************************************************************************/
#include <assert.h>
#include <string.h>
#include <libchdr/flac.h>
#include <libchdr/minmax.h>
#include <retro_miscellaneous.h>
/***************************************************************************
* FLAC DECODER
***************************************************************************
*/
static FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes);
static void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
static FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void* client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
static void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
/*-------------------------------------------------
* flac_decoder - constructor
*-------------------------------------------------
*/
void flac_decoder_init(flac_decoder *decoder)
{
decoder->decoder = FLAC__stream_decoder_new();
decoder->sample_rate = 0;
decoder->channels = 0;
decoder->bits_per_sample = 0;
decoder->compressed_offset = 0;
decoder->compressed_start = NULL;
decoder->compressed_length = 0;
decoder->compressed2_start = NULL;
decoder->compressed2_length = 0;
decoder->uncompressed_offset = 0;
decoder->uncompressed_length = 0;
decoder->uncompressed_swap = 0;
}
/*-------------------------------------------------
* flac_decoder - destructor
*-------------------------------------------------
*/
void flac_decoder_free(flac_decoder* decoder)
{
if ((decoder != NULL) && (decoder->decoder != NULL))
FLAC__stream_decoder_delete(decoder->decoder);
}
/*-------------------------------------------------
* reset - reset state with the original
* parameters
*-------------------------------------------------
*/
static int flac_decoder_internal_reset(flac_decoder* decoder)
{
decoder->compressed_offset = 0;
if (FLAC__stream_decoder_init_stream(decoder->decoder,
&flac_decoder_read_callback_static,
NULL,
&flac_decoder_tell_callback_static,
NULL,
NULL,
&flac_decoder_write_callback_static,
&flac_decoder_metadata_callback_static,
&flac_decoder_error_callback_static, decoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
return 0;
return FLAC__stream_decoder_process_until_end_of_metadata(decoder->decoder);
}
/*-------------------------------------------------
* reset - reset state with new memory parameters
* and a custom-generated header
*-------------------------------------------------
*/
int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length)
{
/* modify the template header with our parameters */
static const uint8_t s_header_template[0x2a] =
{
0x66, 0x4C, 0x61, 0x43, /* +00: 'fLaC' stream header */
0x80, /* +04: metadata block type 0 (STREAMINFO), */
/* flagged as last block */
0x00, 0x00, 0x22, /* +05: metadata block length = 0x22 */
0x00, 0x00, /* +08: minimum block size */
0x00, 0x00, /* +0A: maximum block size */
0x00, 0x00, 0x00, /* +0C: minimum frame size (0 == unknown) */
0x00, 0x00, 0x00, /* +0F: maximum frame size (0 == unknown) */
0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, /* +12: sample rate (0x0ac44 == 44100), */
/* numchannels (2), sample bits (16), */
/* samples in stream (0 == unknown) */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* +1A: MD5 signature (0 == none) */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* +2A: start of stream data */
};
memcpy(decoder->custom_header, s_header_template, sizeof(s_header_template));
decoder->custom_header[0x08] = decoder->custom_header[0x0a] = block_size >> 8;
decoder->custom_header[0x09] = decoder->custom_header[0x0b] = block_size & 0xff;
decoder->custom_header[0x12] = sample_rate >> 12;
decoder->custom_header[0x13] = sample_rate >> 4;
decoder->custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1);
/* configure the header ahead of the provided buffer */
decoder->compressed_start = (const FLAC__byte *)(decoder->custom_header);
decoder->compressed_length = sizeof(decoder->custom_header);
decoder->compressed2_start = (const FLAC__byte *)(buffer);
decoder->compressed2_length = length;
return flac_decoder_internal_reset(decoder);
}
/*-------------------------------------------------
* decode_interleaved - decode to an interleaved
* sound stream
*-------------------------------------------------
*/
int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian)
{
/* configure the uncompressed buffer */
memset(decoder->uncompressed_start, 0, sizeof(decoder->uncompressed_start));
decoder->uncompressed_start[0] = samples;
decoder->uncompressed_offset = 0;
decoder->uncompressed_length = num_samples;
decoder->uncompressed_swap = swap_endian;
/* loop until we get everything we want */
while (decoder->uncompressed_offset < decoder->uncompressed_length)
if (!FLAC__stream_decoder_process_single(decoder->decoder))
return 0;
return 1;
}
#if 0
/*-------------------------------------------------
* decode - decode to an multiple independent
* data streams
*-------------------------------------------------
*/
bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_endian)
{
/* make sure we don't have too many channels */
int chans = channels();
if (chans > ARRAY_SIZE(m_uncompressed_start))
return false;
/* configure the uncompressed buffer */
memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start));
for (int curchan = 0; curchan < chans; curchan++)
m_uncompressed_start[curchan] = samples[curchan];
m_uncompressed_offset = 0;
m_uncompressed_length = num_samples;
m_uncompressed_swap = swap_endian;
/* loop until we get everything we want */
while (m_uncompressed_offset < m_uncompressed_length)
if (!FLAC__stream_decoder_process_single(m_decoder))
return false;
return true;
}
#endif
/*-------------------------------------------------
* finish - finish up the decode
*-------------------------------------------------
*/
uint32_t flac_decoder_finish(flac_decoder* decoder)
{
/* get the final decoding position and move forward */
FLAC__uint64 position = 0;
FLAC__stream_decoder_get_decode_position(decoder->decoder, &position);
FLAC__stream_decoder_finish(decoder->decoder);
/* adjust position if we provided the header */
if (position == 0)
return 0;
if (decoder->compressed_start == (const FLAC__byte *)(decoder->custom_header))
position -= decoder->compressed_length;
return position;
}
/*-------------------------------------------------
* read_callback - handle reads from the input
* stream
*-------------------------------------------------
*/
FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
{
return flac_decoder_read_callback(client_data, buffer, bytes);
}
FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes)
{
flac_decoder* decoder = (flac_decoder*)client_data;
uint32_t expected = *bytes;
/* copy from primary buffer first */
uint32_t outputpos = 0;
if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length)
{
uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed_length - decoder->compressed_offset);
memcpy(&buffer[outputpos], decoder->compressed_start + decoder->compressed_offset, bytes_to_copy);
outputpos += bytes_to_copy;
decoder->compressed_offset += bytes_to_copy;
}
/* once we're out of that, copy from the secondary buffer */
if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length + decoder->compressed2_length)
{
uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed2_length - (decoder->compressed_offset - decoder->compressed_length));
memcpy(&buffer[outputpos], decoder->compressed2_start + decoder->compressed_offset - decoder->compressed_length, bytes_to_copy);
outputpos += bytes_to_copy;
decoder->compressed_offset += bytes_to_copy;
}
*bytes = outputpos;
/* return based on whether we ran out of data */
return (*bytes < expected) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
/*-------------------------------------------------
* metadata_callback - handle STREAMINFO metadata
*-------------------------------------------------
*/
void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
{
flac_decoder *fldecoder;
/* ignore all but STREAMINFO metadata */
if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO)
return;
/* parse out the data we care about */
fldecoder = (flac_decoder *)(client_data);
fldecoder->sample_rate = metadata->data.stream_info.sample_rate;
fldecoder->bits_per_sample = metadata->data.stream_info.bits_per_sample;
fldecoder->channels = metadata->data.stream_info.channels;
}
/*-------------------------------------------------
* tell_callback - handle requests to find out
* where in the input stream we are
*-------------------------------------------------
*/
FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
{
*absolute_byte_offset = ((flac_decoder *)client_data)->compressed_offset;
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
/*-------------------------------------------------
* write_callback - handle writes to the output
* stream
*-------------------------------------------------
*/
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
{
return flac_decoder_write_callback(client_data, frame, buffer);
}
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void *client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
{
int sampnum, chan;
int shift, blocksize;
flac_decoder * decoder = (flac_decoder *)client_data;
assert(frame->header.channels == decoder->channels);
/* interleaved case */
shift = decoder->uncompressed_swap ? 8 : 0;
blocksize = frame->header.blocksize;
if (decoder->uncompressed_start[1] == NULL)
{
int16_t *dest = decoder->uncompressed_start[0] + decoder->uncompressed_offset * frame->header.channels;
for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++)
for (chan = 0; chan < frame->header.channels; chan++)
*dest++ = (int16_t)((((uint16_t)buffer[chan][sampnum]) << shift) | (((uint16_t)buffer[chan][sampnum]) >> shift));
}
/* non-interleaved case */
else
{
for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++)
for (chan = 0; chan < frame->header.channels; chan++)
if (decoder->uncompressed_start[chan] != NULL)
decoder->uncompressed_start[chan][decoder->uncompressed_offset] = (int16_t) ( (((uint16_t)(buffer[chan][sampnum])) << shift) | ( ((uint16_t)(buffer[chan][sampnum])) >> shift) );
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
/**
* @fn void flac_decoder::error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
*
* @brief -------------------------------------------------
* error_callback - handle errors (ignore them)
* -------------------------------------------------.
*
* @param decoder The decoder.
* @param status The status.
* @param [in,out] client_data If non-null, information describing the client.
*/
void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
}

View File

@ -0,0 +1,563 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
****************************************************************************
huffman.c
Static Huffman compression and decompression helpers.
****************************************************************************
Maximum codelength is officially (alphabetsize - 1). This would be 255 bits
(since we use 1 byte values). However, it is also dependent upon the number
of samples used, as follows:
2 bits -> 3..4 samples
3 bits -> 5..7 samples
4 bits -> 8..12 samples
5 bits -> 13..20 samples
6 bits -> 21..33 samples
7 bits -> 34..54 samples
8 bits -> 55..88 samples
9 bits -> 89..143 samples
10 bits -> 144..232 samples
11 bits -> 233..376 samples
12 bits -> 377..609 samples
13 bits -> 610..986 samples
14 bits -> 987..1596 samples
15 bits -> 1597..2583 samples
16 bits -> 2584..4180 samples -> note that a 4k data size guarantees codelength <= 16 bits
17 bits -> 4181..6764 samples
18 bits -> 6765..10945 samples
19 bits -> 10946..17710 samples
20 bits -> 17711..28656 samples
21 bits -> 28657..46367 samples
22 bits -> 46368..75024 samples
23 bits -> 75025..121392 samples
24 bits -> 121393..196417 samples
25 bits -> 196418..317810 samples
26 bits -> 317811..514228 samples
27 bits -> 514229..832039 samples
28 bits -> 832040..1346268 samples
29 bits -> 1346269..2178308 samples
30 bits -> 2178309..3524577 samples
31 bits -> 3524578..5702886 samples
32 bits -> 5702887..9227464 samples
Looking at it differently, here is where powers of 2 fall into these buckets:
256 samples -> 11 bits max
512 samples -> 12 bits max
1k samples -> 14 bits max
2k samples -> 15 bits max
4k samples -> 16 bits max
8k samples -> 18 bits max
16k samples -> 19 bits max
32k samples -> 21 bits max
64k samples -> 22 bits max
128k samples -> 24 bits max
256k samples -> 25 bits max
512k samples -> 27 bits max
1M samples -> 28 bits max
2M samples -> 29 bits max
4M samples -> 31 bits max
8M samples -> 32 bits max
****************************************************************************
Delta-RLE encoding works as follows:
Starting value is assumed to be 0. All data is encoded as a delta
from the previous value, such that final[i] = final[i - 1] + delta.
Long runs of 0s are RLE-encoded as follows:
0x100 = repeat count of 8
0x101 = repeat count of 9
0x102 = repeat count of 10
0x103 = repeat count of 11
0x104 = repeat count of 12
0x105 = repeat count of 13
0x106 = repeat count of 14
0x107 = repeat count of 15
0x108 = repeat count of 16
0x109 = repeat count of 32
0x10a = repeat count of 64
0x10b = repeat count of 128
0x10c = repeat count of 256
0x10d = repeat count of 512
0x10e = repeat count of 1024
0x10f = repeat count of 2048
Note that repeat counts are reset at the end of a row, so if a 0 run
extends to the end of a row, a large repeat count may be used.
The reason for starting the run counts at 8 is that 0 is expected to
be the most common symbol, and is typically encoded in 1 or 2 bits.
***************************************************************************/
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <libchdr/huffman.h>
#include <libchdr/minmax.h>
/***************************************************************************
* MACROS
***************************************************************************
*/
#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f))
/***************************************************************************
* IMPLEMENTATION
***************************************************************************
*/
/*-------------------------------------------------
* huffman_context_base - create an encoding/
* decoding context
*-------------------------------------------------
*/
struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits)
{
struct huffman_decoder* decoder;
/* limit to 24 bits */
if (maxbits > 24)
return NULL;
decoder = (struct huffman_decoder*)malloc(sizeof(struct huffman_decoder));
decoder->numcodes = numcodes;
decoder->maxbits = maxbits;
decoder->lookup = (lookup_value*)malloc(sizeof(lookup_value) * (1 << maxbits));
decoder->huffnode = (struct node_t*)malloc(sizeof(struct node_t) * numcodes);
decoder->datahisto = NULL;
decoder->prevdata = 0;
decoder->rleremaining = 0;
return decoder;
}
void delete_huffman_decoder(struct huffman_decoder* decoder)
{
if (decoder != NULL)
{
if (decoder->lookup != NULL)
free(decoder->lookup);
if (decoder->huffnode != NULL)
free(decoder->huffnode);
free(decoder);
}
}
/*-------------------------------------------------
* decode_one - decode a single code from the
* huffman stream
*-------------------------------------------------
*/
uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf)
{
/* peek ahead to get maxbits worth of data */
uint32_t bits = bitstream_peek(bitbuf, decoder->maxbits);
/* look it up, then remove the actual number of bits for this code */
lookup_value lookup = decoder->lookup[bits];
bitstream_remove(bitbuf, lookup & 0x1f);
/* return the value */
return lookup >> 5;
}
/*-------------------------------------------------
* import_tree_rle - import an RLE-encoded
* huffman tree from a source data stream
*-------------------------------------------------
*/
enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf)
{
enum huffman_error error;
/* bits per entry depends on the maxbits */
int numbits;
int curnode;
if (decoder->maxbits >= 16)
numbits = 5;
else if (decoder->maxbits >= 8)
numbits = 4;
else
numbits = 3;
/* loop until we read all the nodes */
for (curnode = 0; curnode < decoder->numcodes; )
{
/* a non-one value is just raw */
int nodebits = bitstream_read(bitbuf, numbits);
if (nodebits != 1)
decoder->huffnode[curnode++].numbits = nodebits;
/* a one value is an escape code */
else
{
/* a double 1 is just a single 1 */
nodebits = bitstream_read(bitbuf, numbits);
if (nodebits == 1)
decoder->huffnode[curnode++].numbits = nodebits;
/* otherwise, we need one for value for the repeat count */
else
{
int repcount = bitstream_read(bitbuf, numbits) + 3;
while (repcount--)
decoder->huffnode[curnode++].numbits = nodebits;
}
}
}
/* make sure we ended up with the right number */
if (curnode != decoder->numcodes)
return HUFFERR_INVALID_DATA;
/* assign canonical codes for all nodes based on their code lengths */
error = huffman_assign_canonical_codes(decoder);
if (error != HUFFERR_NONE)
return error;
/* build the lookup table */
huffman_build_lookup_table(decoder);
/* determine final input length and report errors */
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
}
/*-------------------------------------------------
* import_tree_huffman - import a huffman-encoded
* huffman tree from a source data stream
*-------------------------------------------------
*/
enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf)
{
int last = 0;
int curcode;
uint32_t temp;
enum huffman_error error;
uint8_t rlefullbits = 0;
int index, count = 0;
int start;
/* start by parsing the lengths for the small tree */
struct huffman_decoder* smallhuff = create_huffman_decoder(24, 6);
smallhuff->huffnode[0].numbits = bitstream_read(bitbuf, 3);
start = bitstream_read(bitbuf, 3) + 1;
for (index = 1; index < 24; index++)
{
if (index < start || count == 7)
smallhuff->huffnode[index].numbits = 0;
else
{
count = bitstream_read(bitbuf, 3);
smallhuff->huffnode[index].numbits = (count == 7) ? 0 : count;
}
}
/* then regenerate the tree */
error = huffman_assign_canonical_codes(smallhuff);
if (error != HUFFERR_NONE)
return error;
huffman_build_lookup_table(smallhuff);
/* determine the maximum length of an RLE count */
temp = decoder->numcodes - 9;
while (temp != 0)
temp >>= 1, rlefullbits++;
/* now process the rest of the data */
for (curcode = 0; curcode < decoder->numcodes; )
{
int value = huffman_decode_one(smallhuff, bitbuf);
if (value != 0)
decoder->huffnode[curcode++].numbits = last = value - 1;
else
{
int count = bitstream_read(bitbuf, 3) + 2;
if (count == 7+2)
count += bitstream_read(bitbuf, rlefullbits);
for ( ; count != 0 && curcode < decoder->numcodes; count--)
decoder->huffnode[curcode++].numbits = last;
}
}
/* make sure we ended up with the right number */
if (curcode != decoder->numcodes)
{
delete_huffman_decoder(smallhuff);
return HUFFERR_INVALID_DATA;
}
/* assign canonical codes for all nodes based on their code lengths */
error = huffman_assign_canonical_codes(decoder);
if (error != HUFFERR_NONE)
{
delete_huffman_decoder(smallhuff);
return error;
}
/* build the lookup table */
huffman_build_lookup_table(decoder);
delete_huffman_decoder(smallhuff);
/* determine final input length and report errors */
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
}
/*-------------------------------------------------
* compute_tree_from_histo - common backend for
* computing a tree based on the data histogram
*-------------------------------------------------
*/
enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder)
{
/* compute the number of data items in the histogram */
int i;
uint32_t upperweight;
uint32_t lowerweight = 0;
uint32_t sdatacount = 0;
for (i = 0; i < decoder->numcodes; i++)
sdatacount += decoder->datahisto[i];
/* binary search to achieve the optimum encoding */
upperweight = sdatacount * 2;
while (1)
{
/* build a tree using the current weight */
uint32_t curweight = (upperweight + lowerweight) / 2;
int curmaxbits = huffman_build_tree(decoder, sdatacount, curweight);
/* apply binary search here */
if (curmaxbits <= decoder->maxbits)
{
lowerweight = curweight;
/* early out if it worked with the raw weights, or if we're done searching */
if (curweight == sdatacount || (upperweight - lowerweight) <= 1)
break;
}
else
upperweight = curweight;
}
/* assign canonical codes for all nodes based on their code lengths */
return huffman_assign_canonical_codes(decoder);
}
/***************************************************************************
* INTERNAL FUNCTIONS
***************************************************************************
*/
/*-------------------------------------------------
* tree_node_compare - compare two tree nodes
* by weight
*-------------------------------------------------
*/
static int huffman_tree_node_compare(const void *item1, const void *item2)
{
const struct node_t *node1 = *(const struct node_t **)item1;
const struct node_t *node2 = *(const struct node_t **)item2;
if (node2->weight != node1->weight)
return node2->weight - node1->weight;
if (node2->bits - node1->bits == 0)
fprintf(stderr, "identical node sort keys, should not happen!\n");
return (int)node1->bits - (int)node2->bits;
}
/*-------------------------------------------------
* build_tree - build a huffman tree based on the
* data distribution
*-------------------------------------------------
*/
int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight)
{
int nextalloc;
int maxbits = 0;
/* make a list of all non-zero nodes */
struct node_t** list = (struct node_t**)
malloc(sizeof(struct node_t*) * decoder->numcodes * 2);
int curcode, listitems = 0;
memset(decoder->huffnode, 0,
decoder->numcodes * sizeof(decoder->huffnode[0]));
for (curcode = 0; curcode < decoder->numcodes; curcode++)
if (decoder->datahisto[curcode] != 0)
{
list[listitems++] = &decoder->huffnode[curcode];
decoder->huffnode[curcode].count = decoder->datahisto[curcode];
decoder->huffnode[curcode].bits = curcode;
/* scale the weight by the current effective length, ensuring we don't go to 0 */
decoder->huffnode[curcode].weight = ((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata);
if (decoder->huffnode[curcode].weight == 0)
decoder->huffnode[curcode].weight = 1;
}
#if 0
{
unsigned i;
fprintf(stderr, "Pre-sort:\n");
for (i = 0; i < listitems; i++)
fprintf(stderr, "weight: %d code: %d\n",
list[i]->m_weight, list[i]->m_bits);
}
#endif
/* sort the list by weight, largest weight first */
qsort(&list[0], listitems, sizeof(list[0]), huffman_tree_node_compare);
#if 0
fprintf(stderr, "Post-sort:\n");
for (int i = 0; i < listitems; i++) {
fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits);
}
fprintf(stderr, "===================\n");
#endif
/* now build the tree */
nextalloc = decoder->numcodes;
while (listitems > 1)
{
int curitem;
/* remove lowest two items */
struct node_t* node1 = &(*list[--listitems]);
struct node_t* node0 = &(*list[--listitems]);
/* create new node */
struct node_t* newnode = &decoder->huffnode[nextalloc++];
newnode->parent = NULL;
node0->parent =
node1->parent = newnode;
newnode->weight =
node0->weight + node1->weight;
/* insert into list at appropriate location */
for (curitem = 0; curitem < listitems; curitem++)
if (newnode->weight > list[curitem]->weight)
{
memmove(&list[curitem+1],
&list[curitem],
(listitems - curitem) * sizeof(list[0]));
break;
}
list[curitem] = newnode;
listitems++;
}
/* compute the number of bits in each code,
* and fill in another histogram */
for (curcode = 0; curcode < decoder->numcodes; curcode++)
{
struct node_t *curnode;
struct node_t* node = &decoder->huffnode[curcode];
node->numbits = 0;
node->bits = 0;
/* if we have a non-zero weight, compute the number of bits */
if (node->weight > 0)
{
/* determine the number of bits for this node */
for (curnode = node;
curnode->parent != NULL; curnode = curnode->parent)
node->numbits++;
if (node->numbits == 0)
node->numbits = 1;
/* keep track of the max */
maxbits = MAX(maxbits, ((int)node->numbits));
}
}
free(list);
return maxbits;
}
/*-------------------------------------------------
* assign_canonical_codes - assign canonical codes
* to all the nodes based on the number of bits
* in each
*-------------------------------------------------
*/
enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder)
{
uint32_t curstart = 0;
/* build up a histogram of bit lengths */
int curcode, codelen;
uint32_t bithisto[33] = { 0 };
for (curcode = 0; curcode < decoder->numcodes; curcode++)
{
struct node_t* node = &decoder->huffnode[curcode];
if (node->numbits > decoder->maxbits)
return HUFFERR_INTERNAL_INCONSISTENCY;
if (node->numbits <= 32)
bithisto[node->numbits]++;
}
/* for each code length, determine the starting code number */
for (codelen = 32; codelen > 0; codelen--)
{
uint32_t nextstart = (curstart + bithisto[codelen]) >> 1;
if (codelen != 1 && nextstart * 2 != (curstart + bithisto[codelen]))
return HUFFERR_INTERNAL_INCONSISTENCY;
bithisto[codelen] = curstart;
curstart = nextstart;
}
/* now assign canonical codes */
for (curcode = 0; curcode < decoder->numcodes; curcode++)
{
struct node_t* node = &decoder->huffnode[curcode];
if (node->numbits > 0)
node->bits = bithisto[node->numbits]++;
}
return HUFFERR_NONE;
}
/*-------------------------------------------------
* build_lookup_table - build a lookup table for
* fast decoding
*-------------------------------------------------
*/
void huffman_build_lookup_table(struct huffman_decoder* decoder)
{
/* iterate over all codes */
int curcode;
for (curcode = 0; curcode < decoder->numcodes; curcode++)
{
/* process all nodes which have non-zero bits */
struct node_t* node = &decoder->huffnode[curcode];
if (node->numbits > 0)
{
int shift;
lookup_value *dest;
lookup_value *destend;
/* set up the entry */
lookup_value value = MAKE_LOOKUP(curcode, node->numbits);
/* fill all matching entries */
shift = decoder->maxbits - node->numbits;
dest = &decoder->lookup[node->bits << shift];
destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
while (dest <= destend)
*dest++ = value;
}
}
}

1194
formats/png/rpng.c Normal file

File diff suppressed because it is too large Load Diff

391
formats/png/rpng_encode.c Normal file
View File

@ -0,0 +1,391 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rpng_encode.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <encodings/crc32.h>
#include <streams/file_stream.h>
#include <streams/trans_stream.h>
#include "rpng_internal.h"
#undef GOTO_END_ERROR
#define GOTO_END_ERROR() do { \
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); \
ret = false; \
goto end; \
} while(0)
static void dword_write_be(uint8_t *buf, uint32_t val)
{
*buf++ = (uint8_t)(val >> 24);
*buf++ = (uint8_t)(val >> 16);
*buf++ = (uint8_t)(val >> 8);
*buf++ = (uint8_t)(val >> 0);
}
static bool png_write_crc(RFILE *file, const uint8_t *data, size_t size)
{
uint8_t crc_raw[4] = {0};
uint32_t crc = encoding_crc32(0, data, size);
dword_write_be(crc_raw, crc);
return filestream_write(file, crc_raw, sizeof(crc_raw)) == sizeof(crc_raw);
}
static bool png_write_ihdr(RFILE *file, const struct png_ihdr *ihdr)
{
uint8_t ihdr_raw[21];
ihdr_raw[0] = '0'; /* Size */
ihdr_raw[1] = '0';
ihdr_raw[2] = '0';
ihdr_raw[3] = '0';
ihdr_raw[4] = 'I';
ihdr_raw[5] = 'H';
ihdr_raw[6] = 'D';
ihdr_raw[7] = 'R';
ihdr_raw[8] = 0; /* Width */
ihdr_raw[9] = 0;
ihdr_raw[10] = 0;
ihdr_raw[11] = 0;
ihdr_raw[12] = 0; /* Height */
ihdr_raw[13] = 0;
ihdr_raw[14] = 0;
ihdr_raw[15] = 0;
ihdr_raw[16] = ihdr->depth; /* Depth */
ihdr_raw[17] = ihdr->color_type;
ihdr_raw[18] = ihdr->compression;
ihdr_raw[19] = ihdr->filter;
ihdr_raw[20] = ihdr->interlace;
dword_write_be(ihdr_raw + 0, sizeof(ihdr_raw) - 8);
dword_write_be(ihdr_raw + 8, ihdr->width);
dword_write_be(ihdr_raw + 12, ihdr->height);
if (filestream_write(file, ihdr_raw, sizeof(ihdr_raw)) != sizeof(ihdr_raw))
return false;
if (!png_write_crc(file, ihdr_raw + sizeof(uint32_t),
sizeof(ihdr_raw) - sizeof(uint32_t)))
return false;
return true;
}
static bool png_write_idat(RFILE *file, const uint8_t *data, size_t size)
{
if (filestream_write(file, data, size) != (ssize_t)size)
return false;
if (!png_write_crc(file, data + sizeof(uint32_t), size - sizeof(uint32_t)))
return false;
return true;
}
static bool png_write_iend(RFILE *file)
{
const uint8_t data[] = {
0, 0, 0, 0,
'I', 'E', 'N', 'D',
};
if (filestream_write(file, data, sizeof(data)) != sizeof(data))
return false;
if (!png_write_crc(file, data + sizeof(uint32_t),
sizeof(data) - sizeof(uint32_t)))
return false;
return true;
}
static void copy_argb_line(uint8_t *dst, const uint32_t *src, unsigned width)
{
unsigned i;
for (i = 0; i < width; i++)
{
uint32_t col = src[i];
*dst++ = (uint8_t)(col >> 16);
*dst++ = (uint8_t)(col >> 8);
*dst++ = (uint8_t)(col >> 0);
*dst++ = (uint8_t)(col >> 24);
}
}
static void copy_bgr24_line(uint8_t *dst, const uint8_t *src, unsigned width)
{
unsigned i;
for (i = 0; i < width; i++, dst += 3, src += 3)
{
dst[2] = src[0];
dst[1] = src[1];
dst[0] = src[2];
}
}
static unsigned count_sad(const uint8_t *data, size_t size)
{
size_t i;
unsigned cnt = 0;
for (i = 0; i < size; i++)
cnt += abs((int8_t)data[i]);
return cnt;
}
static unsigned filter_up(uint8_t *target, const uint8_t *line,
const uint8_t *prev, unsigned width, unsigned bpp)
{
unsigned i;
width *= bpp;
for (i = 0; i < width; i++)
target[i] = line[i] - prev[i];
return count_sad(target, width);
}
static unsigned filter_sub(uint8_t *target, const uint8_t *line,
unsigned width, unsigned bpp)
{
unsigned i;
width *= bpp;
for (i = 0; i < bpp; i++)
target[i] = line[i];
for (i = bpp; i < width; i++)
target[i] = line[i] - line[i - bpp];
return count_sad(target, width);
}
static unsigned filter_avg(uint8_t *target, const uint8_t *line,
const uint8_t *prev, unsigned width, unsigned bpp)
{
unsigned i;
width *= bpp;
for (i = 0; i < bpp; i++)
target[i] = line[i] - (prev[i] >> 1);
for (i = bpp; i < width; i++)
target[i] = line[i] - ((line[i - bpp] + prev[i]) >> 1);
return count_sad(target, width);
}
static unsigned filter_paeth(uint8_t *target,
const uint8_t *line, const uint8_t *prev,
unsigned width, unsigned bpp)
{
unsigned i;
width *= bpp;
for (i = 0; i < bpp; i++)
target[i] = line[i] - paeth(0, prev[i], 0);
for (i = bpp; i < width; i++)
target[i] = line[i] - paeth(line[i - bpp], prev[i], prev[i - bpp]);
return count_sad(target, width);
}
static bool rpng_save_image(const char *path,
const uint8_t *data,
unsigned width, unsigned height, unsigned pitch, unsigned bpp)
{
unsigned h;
bool ret = true;
struct png_ihdr ihdr = {0};
const struct trans_stream_backend *stream_backend = NULL;
size_t encode_buf_size = 0;
uint8_t *encode_buf = NULL;
uint8_t *deflate_buf = NULL;
uint8_t *rgba_line = NULL;
uint8_t *up_filtered = NULL;
uint8_t *sub_filtered = NULL;
uint8_t *avg_filtered = NULL;
uint8_t *paeth_filtered = NULL;
uint8_t *prev_encoded = NULL;
uint8_t *encode_target = NULL;
void *stream = NULL;
uint32_t total_in = 0;
uint32_t total_out = 0;
RFILE *file = filestream_open(path,
RETRO_VFS_FILE_ACCESS_WRITE,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
GOTO_END_ERROR();
stream_backend = trans_stream_get_zlib_deflate_backend();
if (filestream_write(file, png_magic, sizeof(png_magic)) != sizeof(png_magic))
GOTO_END_ERROR();
ihdr.width = width;
ihdr.height = height;
ihdr.depth = 8;
ihdr.color_type = bpp == sizeof(uint32_t) ? 6 : 2; /* RGBA or RGB */
if (!png_write_ihdr(file, &ihdr))
GOTO_END_ERROR();
encode_buf_size = (width * bpp + 1) * height;
encode_buf = (uint8_t*)malloc(encode_buf_size);
if (!encode_buf)
GOTO_END_ERROR();
prev_encoded = (uint8_t*)calloc(1, width * bpp);
if (!prev_encoded)
GOTO_END_ERROR();
rgba_line = (uint8_t*)malloc(width * bpp);
up_filtered = (uint8_t*)malloc(width * bpp);
sub_filtered = (uint8_t*)malloc(width * bpp);
avg_filtered = (uint8_t*)malloc(width * bpp);
paeth_filtered = (uint8_t*)malloc(width * bpp);
if (!rgba_line || !up_filtered || !sub_filtered || !avg_filtered || !paeth_filtered)
GOTO_END_ERROR();
encode_target = encode_buf;
for (h = 0; h < height;
h++, encode_target += width * bpp, data += pitch)
{
if (bpp == sizeof(uint32_t))
copy_argb_line(rgba_line, (const uint32_t*)data, width);
else
copy_bgr24_line(rgba_line, data, width);
/* Try every filtering method, and choose the method
* which has most entries as zero.
*
* This is probably not very optimal, but it's very
* simple to implement.
*/
{
unsigned none_score = count_sad(rgba_line, width * bpp);
unsigned up_score = filter_up(up_filtered, rgba_line, prev_encoded, width, bpp);
unsigned sub_score = filter_sub(sub_filtered, rgba_line, width, bpp);
unsigned avg_score = filter_avg(avg_filtered, rgba_line, prev_encoded, width, bpp);
unsigned paeth_score = filter_paeth(paeth_filtered, rgba_line, prev_encoded, width, bpp);
uint8_t filter = 0;
unsigned min_sad = none_score;
const uint8_t *chosen_filtered = rgba_line;
if (sub_score < min_sad)
{
filter = 1;
chosen_filtered = sub_filtered;
min_sad = sub_score;
}
if (up_score < min_sad)
{
filter = 2;
chosen_filtered = up_filtered;
min_sad = up_score;
}
if (avg_score < min_sad)
{
filter = 3;
chosen_filtered = avg_filtered;
min_sad = avg_score;
}
if (paeth_score < min_sad)
{
filter = 4;
chosen_filtered = paeth_filtered;
}
*encode_target++ = filter;
memcpy(encode_target, chosen_filtered, width * bpp);
memcpy(prev_encoded, rgba_line, width * bpp);
}
}
deflate_buf = (uint8_t*)malloc(encode_buf_size * 2); /* Just to be sure. */
if (!deflate_buf)
GOTO_END_ERROR();
stream = stream_backend->stream_new();
if (!stream)
GOTO_END_ERROR();
stream_backend->set_in(
stream,
encode_buf,
(unsigned)encode_buf_size);
stream_backend->set_out(
stream,
deflate_buf + 8,
(unsigned)(encode_buf_size * 2));
if (!stream_backend->trans(stream, true, &total_in, &total_out, NULL))
{
GOTO_END_ERROR();
}
memcpy(deflate_buf + 4, "IDAT", 4);
dword_write_be(deflate_buf + 0, ((uint32_t)total_out));
if (!png_write_idat(file, deflate_buf, ((size_t)total_out + 8)))
GOTO_END_ERROR();
if (!png_write_iend(file))
GOTO_END_ERROR();
end:
if (file)
filestream_close(file);
free(encode_buf);
free(deflate_buf);
free(rgba_line);
free(prev_encoded);
free(up_filtered);
free(sub_filtered);
free(avg_filtered);
free(paeth_filtered);
if (stream_backend)
{
if (stream)
{
if (stream_backend->stream_free)
stream_backend->stream_free(stream);
}
}
return ret;
}
bool rpng_save_image_argb(const char *path, const uint32_t *data,
unsigned width, unsigned height, unsigned pitch)
{
return rpng_save_image(path, (const uint8_t*)data,
width, height, pitch, sizeof(uint32_t));
}
bool rpng_save_image_bgr24(const char *path, const uint8_t *data,
unsigned width, unsigned height, unsigned pitch)
{
return rpng_save_image(path, (const uint8_t*)data,
width, height, pitch, 3);
}

View File

@ -0,0 +1,56 @@
/* Copyright (C) 2010-2018 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

446
formats/tga/rtga.c Normal file
View File

@ -0,0 +1,446 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rtga.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Modified version of stb_image's TGA sources. */
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h> /* ptrdiff_t on osx */
#include <stdlib.h>
#include <string.h>
#include <retro_assert.h>
#include <retro_inline.h>
#include <formats/image.h>
#include <formats/rtga.h>
#define RTGA_COMPUTE_Y(r, g, b) ((uint8_t)((((r) * 77) + ((g) * 150) + (29 * (b))) >> 8))
struct rtga
{
uint8_t *buff_data;
uint32_t *output_image;
};
typedef struct
{
uint32_t img_x, img_y;
int img_n, img_out_n;
int buflen;
uint8_t buffer_start[128];
uint8_t *img_buffer;
uint8_t *img_buffer_end;
uint8_t *img_buffer_original;
} rtga__context;
static INLINE uint8_t rtga__get8(rtga__context *s)
{
if (s->img_buffer < s->img_buffer_end)
return *s->img_buffer++;
return 0;
}
static void rtga__skip(rtga__context *s, int n)
{
if (n < 0)
{
s->img_buffer = s->img_buffer_end;
return;
}
s->img_buffer += n;
}
static int rtga__getn(rtga__context *s, uint8_t *buffer, int n)
{
if (s->img_buffer+n <= s->img_buffer_end)
{
memcpy(buffer, s->img_buffer, n);
s->img_buffer += n;
return 1;
}
return 0;
}
static int rtga__get16le(rtga__context *s)
{
int z = rtga__get8(s);
return z + (rtga__get8(s) << 8);
}
static unsigned char *rtga__convert_format(
unsigned char *data,
int img_n,
int req_comp,
unsigned int x,
unsigned int y)
{
int i,j;
unsigned char *good = (unsigned char *) malloc(req_comp * x * y);
if (!good)
{
free(data);
return NULL;
}
for (j=0; j < (int) y; ++j)
{
unsigned char *src = data + j * x * img_n ;
unsigned char *dest = good + j * x * req_comp;
switch (((img_n)*8+(req_comp)))
{
case ((1)*8+(2)):
for(i=x-1; i >= 0; --i, src += 1, dest += 2)
dest[0]=src[0], dest[1]=255;
break;
case ((1)*8+(3)):
for(i=x-1; i >= 0; --i, src += 1, dest += 3)
dest[0]=dest[1]=dest[2]=src[0];
break;
case ((1)*8+(4)):
for(i=x-1; i >= 0; --i, src += 1, dest += 4)
dest[0]=dest[1]=dest[2]=src[0], dest[3]=255;
break;
case ((2)*8+(1)):
for(i=x-1; i >= 0; --i, src += 2, dest += 1)
dest[0]=src[0];
break;
case ((2)*8+(3)):
for(i=x-1; i >= 0; --i, src += 2, dest += 3)
dest[0]=dest[1]=dest[2]=src[0];
break;
case ((2)*8+(4)):
for(i=x-1; i >= 0; --i, src += 2, dest += 4)
dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];
break;
case ((3)*8+(4)):
for(i=x-1; i >= 0; --i, src += 3, dest += 4)
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255;
break;
case ((3)*8+(1)):
for(i=x-1; i >= 0; --i, src += 3, dest += 1)
dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]);
break;
case ((3)*8+(2)):
for(i=x-1; i >= 0; --i, src += 3, dest += 2)
dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = 255;
break;
case ((4)*8+(1)):
for(i=x-1; i >= 0; --i, src += 4, dest += 1)
dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]);
break;
case ((4)*8+(2)):
for(i=x-1; i >= 0; --i, src += 4, dest += 2)
dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = src[3];
break;
case ((4)*8+(3)):
for(i=x-1; i >= 0; --i, src += 4, dest += 3)
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];
break;
default:
break;
}
}
free(data);
return good;
}
static uint8_t *rtga__tga_load(rtga__context *s,
unsigned *x, unsigned *y, int *comp, int req_comp)
{
/* Read in the TGA header stuff */
int tga_offset = rtga__get8(s);
int tga_indexed = rtga__get8(s);
int tga_image_type = rtga__get8(s);
int tga_is_RLE = 0;
int tga_palette_start = rtga__get16le(s);
int tga_palette_len = rtga__get16le(s);
int tga_palette_bits = rtga__get8(s);
int tga_x_origin = rtga__get16le(s);
int tga_y_origin = rtga__get16le(s);
int tga_width = rtga__get16le(s);
int tga_height = rtga__get16le(s);
int tga_bits_per_pixel = rtga__get8(s);
int tga_comp = tga_bits_per_pixel / 8;
int tga_inverted = rtga__get8(s);
/* image data */
unsigned char *tga_data = NULL;
(void)tga_palette_start;
(void)tga_x_origin;
(void)tga_y_origin;
/* do a tiny bit of precessing */
if ( tga_image_type >= 8 )
{
tga_image_type -= 8;
tga_is_RLE = 1;
}
/* int tga_alpha_bits = tga_inverted & 15; */
tga_inverted = 1 - ((tga_inverted >> 5) & 1);
/* error check */
if (
(tga_width < 1) || (tga_height < 1) ||
(tga_image_type < 1) || (tga_image_type > 3) ||
(
(tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
(tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)
)
)
return NULL; /* we don't report this as a bad TGA because we don't even know if it's TGA */
/* If paletted, then we will use the number of bits from the palette */
if ( tga_indexed )
tga_comp = tga_palette_bits / 8;
/* TGA info */
*x = tga_width;
*y = tga_height;
if (comp) *comp = tga_comp;
tga_data = (unsigned char*)malloc( (size_t)tga_width * tga_height * tga_comp );
if (!tga_data)
return NULL;
/* skip to the data's starting position (offset usually = 0) */
rtga__skip(s, tga_offset );
if ( !tga_indexed && !tga_is_RLE)
{
int i;
for (i=0; i < tga_height; ++i)
{
int y = tga_inverted ? tga_height -i - 1 : i;
uint8_t *tga_row = tga_data + y*tga_width*tga_comp;
rtga__getn(s, tga_row, tga_width * tga_comp);
}
}
else
{
int i, j;
int RLE_repeating = 0;
int RLE_count = 0;
int read_next_pixel = 1;
unsigned char raw_data[4] = {0};
unsigned char *tga_palette = NULL;
/* Do I need to load a palette? */
if ( tga_indexed)
{
/* any data to skip? (offset usually = 0) */
rtga__skip(s, tga_palette_start );
/* load the palette */
tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 );
if (!tga_palette)
{
free(tga_data);
return NULL;
}
if (!rtga__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 ))
{
free(tga_data);
free(tga_palette);
return NULL;
}
}
/* load the data */
for (i=0; i < tga_width * tga_height; ++i)
{
/* if I'm in RLE mode, do I need to get a RLE rtga__pngchunk? */
if ( tga_is_RLE )
{
if ( RLE_count == 0 )
{
/* yep, get the next byte as a RLE command */
int RLE_cmd = rtga__get8(s);
RLE_count = 1 + (RLE_cmd & 127);
RLE_repeating = RLE_cmd >> 7;
read_next_pixel = 1;
}
else if ( !RLE_repeating )
read_next_pixel = 1;
}
else
read_next_pixel = 1;
/* OK, if I need to read a pixel, do it now */
if ( read_next_pixel )
{
/* load however much data we did have */
if ( tga_indexed )
{
/* read in 1 byte, then perform the lookup */
int pal_idx = rtga__get8(s);
if ( pal_idx >= tga_palette_len ) /* invalid index */
pal_idx = 0;
pal_idx *= tga_bits_per_pixel / 8;
for (j = 0; j*8 < tga_bits_per_pixel; ++j)
raw_data[j] = tga_palette[pal_idx+j];
}
else
{
/* read in the data raw */
for (j = 0; j*8 < tga_bits_per_pixel; ++j)
raw_data[j] = rtga__get8(s);
}
/* clear the reading flag for the next pixel */
read_next_pixel = 0;
} /* end of reading a pixel */
/* copy data */
for (j = 0; j < tga_comp; ++j)
tga_data[i*tga_comp+j] = raw_data[j];
/* in case we're in RLE mode, keep counting down */
--RLE_count;
}
/* do I need to invert the image? */
if ( tga_inverted )
{
for (j = 0; j*2 < tga_height; ++j)
{
int index1 = j * tga_width * tga_comp;
int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
for (i = tga_width * tga_comp; i > 0; --i)
{
unsigned char temp = tga_data[index1];
tga_data[index1] = tga_data[index2];
tga_data[index2] = temp;
++index1;
++index2;
}
}
}
/* Clear my palette, if I had one */
if ( tga_palette != NULL )
free( tga_palette );
}
/* swap RGB */
if (tga_comp >= 3)
{
int i;
unsigned char* tga_pixel = tga_data;
for (i = 0; i < tga_width * tga_height; ++i)
{
unsigned char temp = tga_pixel[0];
tga_pixel[0] = tga_pixel[2];
tga_pixel[2] = temp;
tga_pixel += tga_comp;
}
}
/* convert to target component count */
if ( (req_comp)
&& (req_comp >= 1 && req_comp <= 4)
&& (req_comp != tga_comp))
{
tga_data = rtga__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
}
return tga_data;
}
static uint8_t *rtga_load_from_memory(uint8_t const *buffer, int len,
unsigned *x, unsigned *y, int *comp, int req_comp)
{
rtga__context s;
s.img_buffer = (uint8_t *)buffer;
s.img_buffer_original = (uint8_t *) buffer;
s.img_buffer_end = (uint8_t *) buffer+len;
return rtga__tga_load(&s,x,y,comp,req_comp);
}
int rtga_process_image(rtga_t *rtga, void **buf_data,
size_t size, unsigned *width, unsigned *height)
{
int comp;
unsigned size_tex = 0;
if (!rtga)
return IMAGE_PROCESS_ERROR;
rtga->output_image = (uint32_t*)rtga_load_from_memory(rtga->buff_data,
(int)size, width, height, &comp, 4);
*buf_data = rtga->output_image;
size_tex = (*width) * (*height);
/* Convert RGBA to ARGB */
while(size_tex--)
{
unsigned int texel = rtga->output_image[size_tex];
unsigned int A = texel & 0xFF000000;
unsigned int B = texel & 0x00FF0000;
unsigned int G = texel & 0x0000FF00;
unsigned int R = texel & 0x000000FF;
((unsigned int*)rtga->output_image)[size_tex] = A | (R << 16) | G | (B >> 16);
};
return IMAGE_PROCESS_END;
}
bool rtga_set_buf_ptr(rtga_t *rtga, void *data)
{
if (!rtga)
return false;
rtga->buff_data = (uint8_t*)data;
return true;
}
void rtga_free(rtga_t *rtga)
{
if (!rtga)
return;
free(rtga);
}
rtga_t *rtga_alloc(void)
{
rtga_t *rtga = (rtga_t*)calloc(1, sizeof(*rtga));
if (!rtga)
return NULL;
return rtga;
}

186
formats/wav/rwav.c Normal file
View File

@ -0,0 +1,186 @@
/* Copyright (C) 2010-2018 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);
}

488
formats/xml/rxml.c Normal file
View File

@ -0,0 +1,488 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rxml.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <boolean.h>
#include <streams/file_stream.h>
#include <compat/posix_string.h>
#include <string/stdstring.h>
#include <formats/rxml.h>
struct rxml_document
{
struct rxml_node *root_node;
};
struct rxml_node *rxml_root_node(rxml_document_t *doc)
{
if (doc)
return doc->root_node;
return NULL;
}
static void rxml_free_node(struct rxml_node *node)
{
struct rxml_node *head = NULL;
struct rxml_attrib_node *attrib_node_head = NULL;
if (!node)
return;
for (head = node->children; head; )
{
struct rxml_node *next_node = (struct rxml_node*)head->next;
rxml_free_node(head);
head = next_node;
}
for (attrib_node_head = node->attrib; attrib_node_head; )
{
struct rxml_attrib_node *next_attrib = NULL;
if (!attrib_node_head)
continue;
next_attrib = (struct rxml_attrib_node*)attrib_node_head->next;
if (!next_attrib)
continue;
if (attrib_node_head->attrib)
free(attrib_node_head->attrib);
if (attrib_node_head->value)
free(attrib_node_head->value);
if (attrib_node_head)
free(attrib_node_head);
attrib_node_head = next_attrib;
}
if (node->name)
free(node->name);
if (node->data)
free(node->data);
if (node)
free(node);
}
static bool validate_header(const char **ptr)
{
if (memcmp(*ptr, "<?xml", 5) == 0)
{
const char *eol = strstr(*ptr, "?>\n");
if (!eol)
return false;
/* Always use UTF-8. Don't really care to check. */
*ptr = eol + 3;
return true;
}
return true;
}
static bool range_is_space(const char *begin, const char *end)
{
for (; begin < end; begin++)
if (!isspace(*begin))
return false;
return true;
}
static void skip_spaces(const char **ptr_)
{
const char *ptr = *ptr_;
while (isspace(*ptr))
ptr++;
*ptr_ = ptr;
}
static char *strdup_range(const char *begin, const char *end)
{
ptrdiff_t len = end - begin;
char *ret = (char*)malloc(len + 1);
if (!ret)
return NULL;
memcpy(ret, begin, len);
ret[len] = '\0';
return ret;
}
static char *strdup_range_escape(const char *begin, const char *end)
{
/* Escaping is ignored. Assume we don't deal with that. */
return strdup_range(begin, end);
}
static struct rxml_attrib_node *rxml_parse_attrs(const char *str)
{
char *copy = strdup(str);
if (!copy)
return NULL;
char *last_char = copy + strlen(copy) - 1;
if (*last_char == '/')
*last_char = '\0';
struct rxml_attrib_node *list = NULL;
struct rxml_attrib_node *tail = NULL;
char *attrib = NULL;
char *value = NULL;
char *save;
const char *elem = strtok_r(copy, " \n\t\f\v\r", &save);
while (elem)
{
const char *eq = strstr(elem, "=\"");
if (!eq)
goto end;
const char *end = strrchr(eq + 2, '\"');
if (!end || end != (elem + strlen(elem) - 1))
goto end;
attrib = strdup_range_escape(elem, eq);
value = strdup_range_escape(eq + 2, end);
if (!attrib || !value)
goto end;
struct rxml_attrib_node *new_node =
(struct rxml_attrib_node*)calloc(1, sizeof(*new_node));
if (!new_node)
goto end;
new_node->attrib = attrib;
new_node->value = value;
attrib = NULL;
value = NULL;
if (tail)
{
tail->next = new_node;
tail = new_node;
}
else
list = tail = new_node;
elem = strtok_r(NULL, " \n\t\f\v\r", &save);
}
end:
if (copy)
free(copy);
if (attrib)
free(attrib);
if (value)
free(value);
return list;
}
static char *find_first_space(const char *str)
{
while (*str && !isspace(*str))
str++;
return isspace(*str) ? (char*)str : NULL;
}
static bool rxml_parse_tag(struct rxml_node *node, const char *str)
{
const char *str_ptr = str;
skip_spaces(&str_ptr);
const char *name_end = find_first_space(str_ptr);
if (name_end)
{
node->name = strdup_range(str_ptr, name_end);
if (!node->name || !*node->name)
return false;
node->attrib = rxml_parse_attrs(name_end);
return true;
}
else
{
node->name = strdup(str_ptr);
return node->name && *node->name;
}
}
static struct rxml_node *rxml_parse_node(const char **ptr_)
{
const char *ptr = NULL;
const char *closing = NULL;
char *str = NULL;
bool is_closing = false;
struct rxml_node *node = (struct rxml_node*)calloc(1, sizeof(*node));
if (!node)
return NULL;
skip_spaces(ptr_);
ptr = *ptr_;
if (*ptr != '<')
goto error;
closing = strchr(ptr, '>');
if (!closing)
goto error;
str = strdup_range(ptr + 1, closing);
if (!str)
goto error;
if (!rxml_parse_tag(node, str))
goto error;
/* Are spaces between / and > allowed? */
is_closing = strstr(ptr, "/>") + 1 == closing;
/* Look for more data. Either child nodes or data. */
if (!is_closing)
{
size_t closing_tag_size = strlen(node->name) + 4;
char *closing_tag = (char*)malloc(closing_tag_size);
const char *cdata_start = NULL;
const char *child_start = NULL;
const char *closing_start = NULL;
if (!closing_tag)
goto error;
snprintf(closing_tag, closing_tag_size, "</%s>", node->name);
cdata_start = strstr(closing + 1, "<![CDATA[");
child_start = strchr(closing + 1, '<');
closing_start = strstr(closing + 1, closing_tag);
if (!closing_start)
{
free(closing_tag);
goto error;
}
if (cdata_start && range_is_space(closing + 1, cdata_start))
{
/* CDATA section */
const char *cdata_end = strstr(cdata_start, "]]>");
if (!cdata_end)
{
free(closing_tag);
goto error;
}
node->data = strdup_range(cdata_start +
strlen("<![CDATA["), cdata_end);
}
else if (closing_start && closing_start == child_start) /* Simple Data */
node->data = strdup_range(closing + 1, closing_start);
else
{
/* Parse all child nodes. */
struct rxml_node *list = NULL;
struct rxml_node *tail = NULL;
const char *first_start = NULL;
const char *first_closing = NULL;
ptr = child_start;
first_start = strchr(ptr, '<');
first_closing = strstr(ptr, "</");
while (
first_start &&
first_closing &&
(first_start < first_closing)
)
{
struct rxml_node *new_node = rxml_parse_node(&ptr);
if (!new_node)
{
free(closing_tag);
goto error;
}
if (tail)
{
tail->next = new_node;
tail = new_node;
}
else
list = tail = new_node;
first_start = strchr(ptr, '<');
first_closing = strstr(ptr, "</");
}
node->children = list;
closing_start = strstr(ptr, closing_tag);
if (!closing_start)
{
free(closing_tag);
goto error;
}
}
*ptr_ = closing_start + strlen(closing_tag);
free(closing_tag);
}
else
*ptr_ = closing + 1;
if (str)
free(str);
return node;
error:
if (str)
free(str);
rxml_free_node(node);
return NULL;
}
static char *purge_xml_comments(const char *str)
{
size_t len = strlen(str);
char *new_str = (char*)malloc(len + 1);
if (!new_str)
return NULL;
new_str[len] = '\0';
char *copy_dest = new_str;
const char *copy_src = str;
for (;;)
{
ptrdiff_t copy_len;
const char *comment_start = strstr(copy_src, "<!--");
const char *comment_end = strstr(copy_src, "-->");
if (!comment_start || !comment_end)
break;
copy_len = comment_start - copy_src;
memcpy(copy_dest, copy_src, copy_len);
copy_dest += copy_len;
copy_src = comment_end + strlen("-->");
}
/* Avoid strcpy() as OpenBSD is anal and hates you
* for using it even when it's perfectly safe. */
len = strlen(copy_src);
memcpy(copy_dest, copy_src, len);
copy_dest[len] = '\0';
return new_str;
}
rxml_document_t *rxml_load_document(const char *path)
{
char *memory_buffer = NULL;
char *new_memory_buffer = NULL;
const char *mem_ptr = NULL;
long len = 0;
RFILE *file = filestream_open(path,
RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
return NULL;
rxml_document_t *doc = (rxml_document_t*)calloc(1, sizeof(*doc));
if (!doc)
goto error;
len = filestream_get_size(file);
memory_buffer = (char*)malloc(len + 1);
if (!memory_buffer)
goto error;
memory_buffer[len] = '\0';
if (filestream_read(file, memory_buffer, len) != (size_t)len)
goto error;
filestream_close(file);
file = NULL;
mem_ptr = memory_buffer;
if (!validate_header(&mem_ptr))
goto error;
new_memory_buffer = purge_xml_comments(mem_ptr);
if (!new_memory_buffer)
goto error;
free(memory_buffer);
mem_ptr = memory_buffer = new_memory_buffer;
doc->root_node = rxml_parse_node(&mem_ptr);
if (!doc->root_node)
goto error;
free(memory_buffer);
return doc;
error:
free(memory_buffer);
filestream_close(file);
rxml_free_document(doc);
return NULL;
}
void rxml_free_document(rxml_document_t *doc)
{
if (!doc)
return;
if (doc->root_node)
rxml_free_node(doc->root_node);
free(doc);
}
char *rxml_node_attrib(struct rxml_node *node, const char *attrib)
{
struct rxml_attrib_node *attribs = NULL;
for (attribs = node->attrib; attribs; attribs = attribs->next)
{
if (string_is_equal(attrib, attribs->attrib))
return attribs->value;
}
return NULL;
}

27
formats/xml/test/Makefile Normal file
View 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

View File

@ -0,0 +1,67 @@
/* Copyright (C) 2010-2018 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]);
}

335
gfx/gl_capabilities.c Normal file
View File

@ -0,0 +1,335 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (gl_capabilities.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <string.h>
#include <boolean.h>
#include <glsym/glsym.h>
#include <gfx/gl_capabilities.h>
static bool gl_core_context = false;
bool gl_query_core_context_in_use(void)
{
return gl_core_context;
}
void gl_query_core_context_set(bool set)
{
gl_core_context = set;
}
void gl_query_core_context_unset(void)
{
gl_core_context = false;
}
bool gl_query_extension(const char *ext)
{
bool ret = false;
if (gl_query_core_context_in_use())
{
#ifdef GL_NUM_EXTENSIONS
GLint i;
GLint exts = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &exts);
for (i = 0; i < exts; i++)
{
const char *str = (const char*)glGetStringi(GL_EXTENSIONS, i);
if (str && strstr(str, ext))
{
ret = true;
break;
}
}
#endif
}
else
{
const char *str = (const char*)glGetString(GL_EXTENSIONS);
ret = str && strstr(str, ext);
}
return ret;
}
bool gl_check_error(char **error_string)
{
int error = glGetError();
switch (error)
{
case GL_INVALID_ENUM:
*error_string = strdup("GL: Invalid enum.");
break;
case GL_INVALID_VALUE:
*error_string = strdup("GL: Invalid value.");
break;
case GL_INVALID_OPERATION:
*error_string = strdup("GL: Invalid operation.");
break;
case GL_OUT_OF_MEMORY:
*error_string = strdup("GL: Out of memory.");
break;
case GL_NO_ERROR:
return true;
default:
*error_string = strdup("Non specified GL error.");
break;
}
return false;
}
bool gl_check_capability(enum gl_capability_enum enum_idx)
{
unsigned major = 0;
unsigned minor = 0;
const char *vendor = (const char*)glGetString(GL_VENDOR);
const char *renderer = (const char*)glGetString(GL_RENDERER);
const char *version = (const char*)glGetString(GL_VERSION);
#ifdef HAVE_OPENGLES
if (version && sscanf(version, "OpenGL ES %u.%u", &major, &minor) != 2)
#else
if (version && sscanf(version, "%u.%u", &major, &minor) != 2)
#endif
major = minor = 0;
(void)vendor;
(void)renderer;
switch (enum_idx)
{
case GL_CAPS_GLES3_SUPPORTED:
#if defined(HAVE_OPENGLES)
if (major >= 3)
return true;
#endif
break;
case GL_CAPS_EGLIMAGE:
#if defined(HAVE_EGL) && defined(HAVE_OPENGLES)
if (glEGLImageTargetTexture2DOES != NULL)
return true;
#endif
break;
case GL_CAPS_SYNC:
#ifdef HAVE_OPENGLES
if (major >= 3)
return true;
#else
if (gl_query_extension("ARB_sync") &&
glFenceSync && glDeleteSync && glClientWaitSync)
return true;
#endif
break;
case GL_CAPS_MIPMAP:
{
static bool extension_queried = false;
static bool extension = false;
if (!extension_queried)
{
extension = gl_query_extension("ARB_framebuffer_object");
extension_queried = true;
}
if (extension)
return true;
}
break;
case GL_CAPS_VAO:
#ifndef HAVE_OPENGLES
if (!gl_query_core_context_in_use() && !gl_query_extension("ARB_vertex_array_object"))
return false;
if (glGenVertexArrays && glBindVertexArray && glDeleteVertexArrays)
return true;
#endif
break;
case GL_CAPS_FBO:
#if defined(HAVE_PSGL) || defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES3) || defined(HAVE_OPENGLES_3_1) || defined(HAVE_OPENGLES_3_2)
return true;
#else
if ( !gl_query_core_context_in_use()
&& !gl_query_extension("ARB_framebuffer_object")
&& !gl_query_extension("EXT_framebuffer_object"))
return false;
if (gl_query_extension("ARB_framebuffer_object"))
return true;
if (gl_query_extension("EXT_framebuffer_object"))
return true;
if (major >= 3)
return true;
break;
#endif
case GL_CAPS_ARGB8:
#ifdef HAVE_OPENGLES
if (gl_query_extension("OES_rgb8_rgba8")
|| gl_query_extension("ARM_rgba8")
|| major >= 3)
return true;
#else
/* TODO/FIXME - implement this for non-GLES? */
#endif
break;
case GL_CAPS_DEBUG:
if (gl_query_extension("KHR_debug"))
return true;
#ifndef HAVE_OPENGLES
if (gl_query_extension("ARB_debug_output"))
return true;
#endif
break;
case GL_CAPS_PACKED_DEPTH_STENCIL:
if (major >= 3)
return true;
if (gl_query_extension("OES_packed_depth_stencil"))
return true;
if (gl_query_extension("EXT_packed_depth_stencil"))
return true;
break;
case GL_CAPS_ES2_COMPAT:
#ifndef HAVE_OPENGLES
/* ATI card detected, skipping check for GL_RGB565 support... */
if (vendor && renderer && (strstr(vendor, "ATI") || strstr(renderer, "ATI")))
return false;
if (gl_query_extension("ARB_ES2_compatibility"))
return true;
#endif
break;
case GL_CAPS_UNPACK_ROW_LENGTH:
#ifdef HAVE_OPENGLES
if (major >= 3)
return true;
/* Extension GL_EXT_unpack_subimage, can copy textures faster
* than using UNPACK_ROW_LENGTH */
if (gl_query_extension("GL_EXT_unpack_subimage"))
return true;
#endif
break;
case GL_CAPS_FULL_NPOT_SUPPORT:
if (major >= 3)
return true;
#ifdef HAVE_OPENGLES
if (gl_query_extension("ARB_texture_non_power_of_two") ||
gl_query_extension("OES_texture_npot"))
return true;
#else
{
GLint max_texture_size = 0;
GLint max_native_instr = 0;
/* try to detect actual npot support. might fail for older cards. */
bool arb_npot = gl_query_extension("ARB_texture_non_power_of_two");
bool arb_frag_program = gl_query_extension("ARB_fragment_program");
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
#ifdef GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB
if (arb_frag_program && glGetProgramivARB)
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,
GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &max_native_instr);
#endif
if (arb_npot && arb_frag_program &&
(max_texture_size >= 8192) && (max_native_instr >= 4096))
return true;
}
#endif
break;
case GL_CAPS_SRGB_FBO_ES3:
#ifdef HAVE_OPENGLES
if (major >= 3)
return true;
#else
break;
#endif
case GL_CAPS_SRGB_FBO:
#if defined(HAVE_OPENGLES)
if (major >= 3 || gl_query_extension("EXT_sRGB"))
return true;
#endif
if (gl_check_capability(GL_CAPS_FBO))
{
if ( gl_query_core_context_in_use() ||
(gl_query_extension("EXT_texture_sRGB")
&& gl_query_extension("ARB_framebuffer_sRGB"))
)
return true;
}
break;
case GL_CAPS_FP_FBO:
/* GLES - No extensions for float FBO currently. */
#ifndef HAVE_OPENGLES
if (gl_check_capability(GL_CAPS_FBO))
{
/* Float FBO is core in 3.2. */
if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float"))
return true;
}
#endif
break;
case GL_CAPS_BGRA8888:
#ifdef HAVE_OPENGLES
/* There are both APPLE and EXT variants. */
if (gl_query_extension("BGRA8888") && !strstr(renderer, "VideoCore"))
return true;
#else
return true;
#endif
break;
case GL_CAPS_TEX_STORAGE:
#ifdef HAVE_OPENGLES
if (major >= 3)
return true;
#else
if (strstr(vendor, "ATI Technologies"))
return false;
if (gl_query_extension("ARB_texture_storage"))
return true;
#endif
break;
case GL_CAPS_TEX_STORAGE_EXT:
#ifdef TARGET_OS_IPHONE
/* Not working on iOS */
return false;
#else
if (gl_query_extension("EXT_texture_storage"))
return true;
#endif
break;
case GL_CAPS_NONE:
default:
break;
}
return false;
}

817
gfx/scaler/pixconv.c Normal file
View File

@ -0,0 +1,817 @@
/* Copyright (C) 2010-2018 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);
}

347
gfx/scaler/scaler.c Normal file
View File

@ -0,0 +1,347 @@
/* Copyright (C) 2010-2018 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;
case SCALER_FMT_ABGR8888:
ctx->out_pixconv = conv_argb8888_abgr8888;
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);
}

243
gfx/scaler/scaler_filter.c Normal file
View File

@ -0,0 +1,243 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (scaler_filter.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <gfx/scaler/filter.h>
#include <gfx/scaler/scaler_int.h>
#include <retro_inline.h>
#include <filters.h>
#include <retro_math.h>
#define FILTER_UNITY (1 << 14)
static INLINE void gen_filter_point_sub(struct scaler_filter *filter,
int len, int pos, int step)
{
int i;
for (i = 0; i < len; i++, pos += step)
{
filter->filter_pos[i] = pos >> 16;
filter->filter[i] = FILTER_UNITY;
}
}
static INLINE void gen_filter_bilinear_sub(struct scaler_filter *filter,
int len, int pos, int step)
{
int i;
for (i = 0; i < len; i++, pos += step)
{
filter->filter_pos[i] = pos >> 16;
filter->filter[i * 2 + 1] = (pos & 0xffff) >> 2;
filter->filter[i * 2 + 0] = FILTER_UNITY - filter->filter[i * 2 + 1];
}
}
static INLINE void gen_filter_sinc_sub(struct scaler_filter *filter,
int len, int pos, int step, double phase_mul)
{
int i, j;
const int sinc_size = filter->filter_len;
for (i = 0; i < len; i++, pos += step)
{
filter->filter_pos[i] = pos >> 16;
for (j = 0; j < sinc_size; j++)
{
double sinc_phase = M_PI * ((double)((sinc_size << 15) + (pos & 0xffff)) / 0x10000 - j);
double lanczos_phase = sinc_phase / ((sinc_size >> 1));
int16_t sinc_val = FILTER_UNITY * sinc(sinc_phase * phase_mul) * sinc(lanczos_phase) * phase_mul;
filter->filter[i * sinc_size + j] = sinc_val;
}
}
}
static bool validate_filter(struct scaler_ctx *ctx)
{
int i;
int max_h_pos;
int max_w_pos = ctx->in_width - ctx->horiz.filter_len;
for (i = 0; i < ctx->out_width; i++)
{
if (ctx->horiz.filter_pos[i] > max_w_pos || ctx->horiz.filter_pos[i] < 0)
{
fprintf(stderr, "Out X = %d => In X = %d\n", i, ctx->horiz.filter_pos[i]);
return false;
}
}
max_h_pos = ctx->in_height - ctx->vert.filter_len;
for (i = 0; i < ctx->out_height; i++)
{
if (ctx->vert.filter_pos[i] > max_h_pos || ctx->vert.filter_pos[i] < 0)
{
fprintf(stderr, "Out Y = %d => In Y = %d\n", i, ctx->vert.filter_pos[i]);
return false;
}
}
return true;
}
static void fixup_filter_sub(struct scaler_filter *filter,
int out_len, int in_len)
{
int i;
int max_pos = in_len - filter->filter_len;
for (i = 0; i < out_len; i++)
{
int postsample = filter->filter_pos[i] - max_pos;
int presample = -filter->filter_pos[i];
if (postsample > 0)
{
int16_t *base_filter = NULL;
filter->filter_pos[i] -= postsample;
base_filter = filter->filter + i * filter->filter_stride;
if (postsample > (int)filter->filter_len)
memset(base_filter, 0, filter->filter_len * sizeof(int16_t));
else
{
memmove(base_filter + postsample, base_filter,
(filter->filter_len - postsample) * sizeof(int16_t));
memset(base_filter, 0, postsample * sizeof(int16_t));
}
}
if (presample > 0)
{
int16_t *base_filter = NULL;
filter->filter_pos[i] += presample;
base_filter = filter->filter + i * filter->filter_stride;
if (presample > (int)filter->filter_len)
memset(base_filter, 0, filter->filter_len * sizeof(int16_t));
else
{
memmove(base_filter, base_filter + presample,
(filter->filter_len - presample) * sizeof(int16_t));
memset(base_filter + (filter->filter_len - presample),
0, presample * sizeof(int16_t));
}
}
}
}
bool scaler_gen_filter(struct scaler_ctx *ctx)
{
int x_pos, x_step, y_pos, y_step;
int sinc_size = 0;
switch (ctx->scaler_type)
{
case SCALER_TYPE_POINT:
ctx->horiz.filter_len = 1;
ctx->horiz.filter_stride = 1;
ctx->vert.filter_len = 1;
ctx->vert.filter_stride = 1;
break;
case SCALER_TYPE_BILINEAR:
ctx->horiz.filter_len = 2;
ctx->horiz.filter_stride = 2;
ctx->vert.filter_len = 2;
ctx->vert.filter_stride = 2;
break;
case SCALER_TYPE_SINC:
sinc_size = 8 * ((ctx->in_width > ctx->out_width)
? next_pow2(ctx->in_width / ctx->out_width) : 1);
ctx->horiz.filter_len = sinc_size;
ctx->horiz.filter_stride = sinc_size;
ctx->vert.filter_len = sinc_size;
ctx->vert.filter_stride = sinc_size;
break;
case SCALER_TYPE_UNKNOWN:
default:
return false;
}
ctx->horiz.filter = (int16_t*)calloc(sizeof(int16_t), ctx->horiz.filter_stride * ctx->out_width);
ctx->horiz.filter_pos = (int*)calloc(sizeof(int), ctx->out_width);
ctx->vert.filter = (int16_t*)calloc(sizeof(int16_t), ctx->vert.filter_stride * ctx->out_height);
ctx->vert.filter_pos = (int*)calloc(sizeof(int), ctx->out_height);
if (!ctx->horiz.filter || !ctx->vert.filter)
return false;
x_step = (1 << 16) * ctx->in_width / ctx->out_width;
y_step = (1 << 16) * ctx->in_height / ctx->out_height;
switch (ctx->scaler_type)
{
case SCALER_TYPE_POINT:
x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15);
y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15);
gen_filter_point_sub(&ctx->horiz, ctx->out_width, x_pos, x_step);
gen_filter_point_sub(&ctx->vert, ctx->out_height, y_pos, y_step);
ctx->scaler_special = scaler_argb8888_point_special;
break;
case SCALER_TYPE_BILINEAR:
x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15);
y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15);
gen_filter_bilinear_sub(&ctx->horiz, ctx->out_width, x_pos, x_step);
gen_filter_bilinear_sub(&ctx->vert, ctx->out_height, y_pos, y_step);
break;
case SCALER_TYPE_SINC:
/* Need to expand the filter when downsampling
* to get a proper low-pass effect. */
x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15) - (sinc_size << 15);
y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15) - (sinc_size << 15);
gen_filter_sinc_sub(&ctx->horiz, ctx->out_width, x_pos, x_step,
ctx->in_width > ctx->out_width ? (double)ctx->out_width / ctx->in_width : 1.0);
gen_filter_sinc_sub(&ctx->vert, ctx->out_height, y_pos, y_step,
ctx->in_height > ctx->out_height ? (double)ctx->out_height / ctx->in_height : 1.0
);
break;
case SCALER_TYPE_UNKNOWN:
break;
}
/* Makes sure that we never sample outside our rectangle. */
fixup_filter_sub(&ctx->horiz, ctx->out_width, ctx->in_width);
fixup_filter_sub(&ctx->vert, ctx->out_height, ctx->in_height);
return validate_filter(ctx);
}

261
gfx/scaler/scaler_int.c Normal file
View File

@ -0,0 +1,261 @@
/* Copyright (C) 2010-2018 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
glsm/glsm.c Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More