build: Merge submodules into repo
This commit is contained in:
parent
540a30f719
commit
4c64279f90
422 changed files with 106715 additions and 8 deletions
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 458bcd842bc48f730e12732fe8b3280e834d45ff
|
||||
6
src/minarch/libretro-common/.gitignore
vendored
Normal file
6
src/minarch/libretro-common/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
glsm/
|
||||
*.[od]
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.exe
|
||||
58
src/minarch/libretro-common/Makefile.test
Normal file
58
src/minarch/libretro-common/Makefile.test
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
OBJDIR = ../obj-unix
|
||||
|
||||
TEST_UNIT_CFLAGS = $(CFLAGS) -Iinclude $(LDFLAGS) -lcheck $(LIBCHECK_CFLAGS) -Werror -Wdeclaration-after-statement -fsanitize=address -fsanitize=undefined -ftest-coverage -fprofile-arcs -ggdb
|
||||
|
||||
TEST_GENERIC_QUEUE = test/queues/test_generic_queue
|
||||
TEST_GENERIC_QUEUE_SRC = test/queues/test_generic_queue.c queues/generic_queue.c
|
||||
|
||||
TEST_LINKED_LIST = test/lists/test_linked_list
|
||||
TEST_LINKED_LIST_SRC = test/lists/test_linked_list.c lists/linked_list.c
|
||||
|
||||
TEST_STDSTRING = test/string/test_stdstring
|
||||
TEST_STDSTRING_SRC = test/string/test_stdstring.c string/stdstring.c encodings/encoding_utf.c \
|
||||
compat/compat_strl.c
|
||||
|
||||
TEST_UTILS = test/utils/test_utils
|
||||
TEST_UTILS_SRC = test/utils/test_utils.c utils/md5.c encodings/encoding_crc32.c \
|
||||
streams/file_stream.c vfs/vfs_implementation.c file/file_path.c \
|
||||
compat/compat_strl.c time/rtime.c string/stdstring.c encodings/encoding_utf.c
|
||||
|
||||
TEST_HASH = test/hash/test_hash
|
||||
TEST_HASH_SRC = test/hash/test_hash.c hash/lrc_hash.c \
|
||||
streams/file_stream.c vfs/vfs_implementation.c file/file_path.c \
|
||||
compat/compat_strl.c time/rtime.c string/stdstring.c encodings/encoding_utf.c
|
||||
|
||||
all:
|
||||
# Build and execute tests in order, to avoid coverage file collision
|
||||
# string
|
||||
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_STDSTRING_SRC) -o $(TEST_STDSTRING)
|
||||
$(TEST_STDSTRING)
|
||||
lcov -c -d . -o `dirname $(TEST_STDSTRING)`/coverage.info
|
||||
# utils
|
||||
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_UTILS_SRC) -o $(TEST_UTILS)
|
||||
$(TEST_UTILS)
|
||||
lcov -c -d . -o `dirname $(TEST_UTILS)`/coverage.info
|
||||
# utils
|
||||
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_HASH_SRC) -o $(TEST_HASH)
|
||||
$(TEST_HASH)
|
||||
lcov -c -d . -o `dirname $(TEST_HASH)`/coverage.info
|
||||
# list
|
||||
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_LINKED_LIST_SRC) -o $(TEST_LINKED_LIST)
|
||||
$(TEST_LINKED_LIST)
|
||||
lcov -c -d . -o `dirname $(TEST_LINKED_LIST)`/coverage.info
|
||||
# queue
|
||||
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_GENERIC_QUEUE_SRC) -o $(TEST_GENERIC_QUEUE)
|
||||
$(TEST_GENERIC_QUEUE)
|
||||
lcov -c -d . -o `dirname $(TEST_GENERIC_QUEUE)`/coverage.info
|
||||
|
||||
lcov -o test/coverage.info \
|
||||
-a test/utils/coverage.info \
|
||||
-a test/string/coverage.info \
|
||||
-a test/lists/coverage.info \
|
||||
-a test/queues/coverage.info
|
||||
genhtml -o test/coverage/ test/coverage.info
|
||||
|
||||
clean:
|
||||
rm -f *.gcda *.gcno
|
||||
|
||||
377
src/minarch/libretro-common/audio/audio_mix.c
Normal file
377
src/minarch/libretro-common/audio/audio_mix.c
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/* Copyright (C) 2010-2020 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <memalign.h>
|
||||
|
||||
#include <retro_environment.h>
|
||||
|
||||
#if defined(__SSE2__)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(__ALTIVEC__)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
#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, remaining_samples;
|
||||
__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]));
|
||||
}
|
||||
|
||||
remaining_samples = samples - i;
|
||||
|
||||
for (i = 0; i < remaining_samples; i++)
|
||||
out[i] += in[i] * vol;
|
||||
}
|
||||
#endif
|
||||
|
||||
void audio_mix_free_chunk(audio_chunk_t *chunk)
|
||||
{
|
||||
if (!chunk)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav && chunk->rwav->samples)
|
||||
{
|
||||
/* rwav_free only frees the samples */
|
||||
rwav_free(chunk->rwav);
|
||||
free(chunk->rwav);
|
||||
}
|
||||
#endif
|
||||
|
||||
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,
|
||||
const char *resampler_ident, enum resampler_quality quality)
|
||||
{
|
||||
#ifdef HAVE_RWAV
|
||||
int sample_size;
|
||||
int64_t len = 0;
|
||||
void *buf = NULL;
|
||||
audio_chunk_t *chunk = (audio_chunk_t*)malloc(sizeof(*chunk));
|
||||
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
|
||||
chunk->buf = NULL;
|
||||
chunk->upsample_buf = NULL;
|
||||
chunk->float_buf = NULL;
|
||||
chunk->float_resample_buf = NULL;
|
||||
chunk->resample_buf = NULL;
|
||||
chunk->len = 0;
|
||||
chunk->resample_len = 0;
|
||||
chunk->sample_rate = sample_rate;
|
||||
chunk->resample = false;
|
||||
chunk->resampler = NULL;
|
||||
chunk->resampler_data = NULL;
|
||||
chunk->ratio = 0.00f;
|
||||
chunk->rwav = (rwav_t*)malloc(sizeof(rwav_t));
|
||||
|
||||
if (!chunk->rwav)
|
||||
goto error;
|
||||
|
||||
chunk->rwav->bitspersample = 0;
|
||||
chunk->rwav->numchannels = 0;
|
||||
chunk->rwav->samplerate = 0;
|
||||
chunk->rwav->numsamples = 0;
|
||||
chunk->rwav->subchunk2size = 0;
|
||||
chunk->rwav->samples = NULL;
|
||||
|
||||
if (!filestream_read_file(path, &buf, &len))
|
||||
goto error;
|
||||
|
||||
chunk->buf = buf;
|
||||
chunk->len = len;
|
||||
|
||||
if (rwav_load(chunk->rwav, chunk->buf, chunk->len) == RWAV_ITERATE_ERROR)
|
||||
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;
|
||||
|
||||
if (chunk->rwav->numchannels == 1)
|
||||
{
|
||||
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||
{
|
||||
uint8_t *sample = (
|
||||
(uint8_t*)chunk->rwav->samples) + i;
|
||||
|
||||
chunk->upsample_buf[i * 2] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
chunk->upsample_buf[(i * 2) + 1] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
}
|
||||
}
|
||||
else if (chunk->rwav->numchannels == 2)
|
||||
{
|
||||
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||
{
|
||||
uint8_t *sample = (
|
||||
(uint8_t*)chunk->rwav->samples) +
|
||||
(i * 2);
|
||||
|
||||
chunk->upsample_buf[i * 2] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
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 */
|
||||
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,
|
||||
resampler_ident,
|
||||
quality,
|
||||
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);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk)
|
||||
{
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
{
|
||||
if (chunk->resample)
|
||||
return chunk->resample_len;
|
||||
return chunk->rwav->numsamples;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* no other filetypes supported yet */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk)
|
||||
{
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
{
|
||||
int16_t *sample;
|
||||
|
||||
if (chunk->resample)
|
||||
sample = chunk->resample_buf;
|
||||
else
|
||||
sample = chunk->upsample_buf;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk)
|
||||
{
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
return chunk->rwav->numchannels;
|
||||
#endif
|
||||
|
||||
/* don't support other formats yet */
|
||||
return 0;
|
||||
}
|
||||
1400
src/minarch/libretro-common/audio/audio_mixer.c
Normal file
1400
src/minarch/libretro-common/audio/audio_mixer.c
Normal file
File diff suppressed because it is too large
Load diff
178
src/minarch/libretro-common/audio/conversion/float_to_s16.c
Normal file
178
src/minarch/libretro-common/audio/conversion/float_to_s16.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/* Copyright (C) 2010-2021 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(HAVE_NEON))
|
||||
static bool float_to_s16_neon_enabled = false;
|
||||
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
|
||||
void convert_float_s16_asm(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
#else
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
void convert_float_to_s16(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
size_t i = 0;
|
||||
if (float_to_s16_neon_enabled)
|
||||
{
|
||||
float gf = (1<<15);
|
||||
float32x4_t vgf = {gf, gf, gf, gf};
|
||||
while (samples >= 8)
|
||||
{
|
||||
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
|
||||
size_t aligned_samples = samples & ~7;
|
||||
if (aligned_samples)
|
||||
convert_float_s16_asm(out, in, aligned_samples);
|
||||
|
||||
out += aligned_samples;
|
||||
in += aligned_samples;
|
||||
samples -= aligned_samples;
|
||||
i = 0;
|
||||
#else
|
||||
int16x4x2_t oreg;
|
||||
int32x4x2_t creg;
|
||||
float32x4x2_t inreg = vld2q_f32(in);
|
||||
creg.val[0] = vcvtq_s32_f32(vmulq_f32(inreg.val[0], vgf));
|
||||
creg.val[1] = vcvtq_s32_f32(vmulq_f32(inreg.val[1], vgf));
|
||||
oreg.val[0] = vqmovn_s32(creg.val[0]);
|
||||
oreg.val[1] = vqmovn_s32(creg.val[1]);
|
||||
vst2_s16(out, oreg);
|
||||
in += 8;
|
||||
out += 8;
|
||||
samples -= 8;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < samples; i++)
|
||||
{
|
||||
int32_t val = (int32_t)(in[i] * 0x8000);
|
||||
out[i] = (val > 0x7FFF) ? 0x7FFF :
|
||||
(val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||
}
|
||||
}
|
||||
|
||||
void convert_float_to_s16_init_simd(void)
|
||||
{
|
||||
uint64_t cpu = cpu_features_get();
|
||||
|
||||
if (cpu & RETRO_SIMD_NEON)
|
||||
float_to_s16_neon_enabled = true;
|
||||
}
|
||||
#else
|
||||
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(_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);
|
||||
}
|
||||
}
|
||||
|
||||
void convert_float_to_s16_init_simd(void) { }
|
||||
#endif
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/* Copyright (C) 2010-2020 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__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#ifndef __MACH__
|
||||
.arm
|
||||
#endif
|
||||
|
||||
.align 4
|
||||
.globl convert_float_s16_asm
|
||||
#ifndef __MACH__
|
||||
.type convert_float_s16_asm, %function
|
||||
#endif
|
||||
.globl _convert_float_s16_asm
|
||||
#ifndef __MACH__
|
||||
.type _convert_float_s16_asm, %function
|
||||
#endif
|
||||
# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)
|
||||
convert_float_s16_asm:
|
||||
_convert_float_s16_asm:
|
||||
# Hacky way to get a constant of 2^15.
|
||||
# ((2^4)^2)^2 * 0.5 = 2^15
|
||||
vmov.f32 q8, #16.0
|
||||
vmov.f32 q9, #0.5
|
||||
vmul.f32 q8, q8, q8
|
||||
vmul.f32 q8, q8, q8
|
||||
vmul.f32 q8, q8, q9
|
||||
|
||||
1:
|
||||
# Preload here?
|
||||
vld1.f32 {q0-q1}, [r1]!
|
||||
|
||||
vmul.f32 q0, q0, q8
|
||||
vmul.f32 q1, q1, q8
|
||||
|
||||
vcvt.s32.f32 q0, q0
|
||||
vcvt.s32.f32 q1, q1
|
||||
|
||||
vqmovn.s32 d4, q0
|
||||
vqmovn.s32 d5, q1
|
||||
|
||||
vst1.f32 {d4-d5}, [r0]!
|
||||
|
||||
# Guaranteed to get samples in multiples of 8.
|
||||
subs r2, r2, #8
|
||||
bne 1b
|
||||
|
||||
bx lr
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/* Copyright (C) 2010-2020 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__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#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
|
||||
205
src/minarch/libretro-common/audio/conversion/s16_to_float.c
Normal file
205
src/minarch/libretro-common/audio/conversion/s16_to_float.c
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/* Copyright (C) 2010-2021 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(HAVE_NEON))
|
||||
static bool s16_to_float_neon_enabled = false;
|
||||
|
||||
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
|
||||
/* 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);
|
||||
#else
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
void convert_s16_to_float(float *out,
|
||||
const int16_t *in, size_t samples, float gain)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
if (s16_to_float_neon_enabled)
|
||||
{
|
||||
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
|
||||
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 += aligned_samples;
|
||||
in += aligned_samples;
|
||||
samples -= aligned_samples;
|
||||
i = 0;
|
||||
#else
|
||||
float gf = gain / (1 << 15);
|
||||
float32x4_t vgf = {gf, gf, gf, gf};
|
||||
while (samples >= 8)
|
||||
{
|
||||
float32x4x2_t oreg;
|
||||
int16x4x2_t inreg = vld2_s16(in);
|
||||
int32x4_t p1 = vmovl_s16(inreg.val[0]);
|
||||
int32x4_t p2 = vmovl_s16(inreg.val[1]);
|
||||
oreg.val[0] = vmulq_f32(vcvtq_f32_s32(p1), vgf);
|
||||
oreg.val[1] = vmulq_f32(vcvtq_f32_s32(p2), vgf);
|
||||
vst2q_f32(out, oreg);
|
||||
in += 8;
|
||||
out += 8;
|
||||
samples -= 8;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gain /= 0x8000;
|
||||
|
||||
for (; i < samples; i++)
|
||||
out[i] = (float)in[i] * gain;
|
||||
}
|
||||
|
||||
void convert_s16_to_float_init_simd(void)
|
||||
{
|
||||
uint64_t cpu = cpu_features_get();
|
||||
|
||||
if (cpu & RETRO_SIMD_NEON)
|
||||
s16_to_float_neon_enabled = true;
|
||||
}
|
||||
#else
|
||||
void convert_s16_to_float(float *out,
|
||||
const int16_t *in, size_t samples, float gain)
|
||||
{
|
||||
unsigned 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)
|
||||
{
|
||||
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;
|
||||
#endif
|
||||
|
||||
gain /= 0x8000;
|
||||
|
||||
#if 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
|
||||
|
||||
__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
|
||||
|
||||
for (; i < samples; i++)
|
||||
out[i] = (float)in[i] * gain;
|
||||
}
|
||||
|
||||
void convert_s16_to_float_init_simd(void) { }
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/* Copyright (C) 2010-2020 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__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#ifndef __MACH__
|
||||
.arm
|
||||
#endif
|
||||
|
||||
.align 4
|
||||
.globl convert_s16_float_asm
|
||||
#ifndef __MACH__
|
||||
.type convert_s16_float_asm, %function
|
||||
#endif
|
||||
.globl _convert_s16_float_asm
|
||||
#ifndef __MACH__
|
||||
.type _convert_s16_float_asm, %function
|
||||
#endif
|
||||
# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)
|
||||
convert_s16_float_asm:
|
||||
_convert_s16_float_asm:
|
||||
# Hacky way to get a constant of 2^-15.
|
||||
# Might be faster to just load a constant from memory.
|
||||
# It's just done once however ...
|
||||
vmov.f32 q8, #0.25
|
||||
vmul.f32 q8, q8, q8
|
||||
vmul.f32 q8, q8, q8
|
||||
vmul.f32 q8, q8, q8
|
||||
vadd.f32 q8, q8, q8
|
||||
|
||||
# Apply gain
|
||||
vld1.f32 {d6[0]}, [r3]
|
||||
vmul.f32 q8, q8, d6[0]
|
||||
|
||||
1:
|
||||
# Preload here?
|
||||
vld1.s16 {q0}, [r1]!
|
||||
|
||||
# Widen to 32-bit
|
||||
vmovl.s16 q1, d0
|
||||
vmovl.s16 q2, d1
|
||||
|
||||
# Convert to float
|
||||
vcvt.f32.s32 q1, q1
|
||||
vcvt.f32.s32 q2, q2
|
||||
|
||||
vmul.f32 q1, q1, q8
|
||||
vmul.f32 q2, q2, q8
|
||||
|
||||
vst1.f32 {q1-q2}, [r0]!
|
||||
|
||||
# Guaranteed to get samples in multiples of 8.
|
||||
subs r2, r2, #8
|
||||
bne 1b
|
||||
|
||||
bx lr
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/* Copyright (C) 2010-2020 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__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#if defined(__thumb__)
|
||||
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
|
||||
#else
|
||||
#define DECL_ARMMODE(x) " .align 4\n" " .global " x "\n" " .arm\n" x ":\n"
|
||||
#endif
|
||||
|
||||
asm(
|
||||
DECL_ARMMODE("convert_s16_float_asm")
|
||||
DECL_ARMMODE("_convert_s16_float_asm")
|
||||
"# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)\n"
|
||||
" # Hacky way to get a constant of 2^-15.\n"
|
||||
" # Might be faster to just load a constant from memory.\n"
|
||||
" # It's just done once however ...\n"
|
||||
" vmov.f32 q8, #0.25\n"
|
||||
" vmul.f32 q8, q8, q8\n"
|
||||
" vmul.f32 q8, q8, q8\n"
|
||||
" vmul.f32 q8, q8, q8\n"
|
||||
" vadd.f32 q8, q8, q8\n"
|
||||
"\n"
|
||||
" # Apply gain\n"
|
||||
" vld1.f32 {d6[0]}, [r3]\n"
|
||||
" vmul.f32 q8, q8, d6[0]\n"
|
||||
"\n"
|
||||
"1:\n"
|
||||
" # Preload here?\n"
|
||||
" vld1.s16 {q0}, [r1]!\n"
|
||||
"\n"
|
||||
" # Widen to 32-bit\n"
|
||||
" vmovl.s16 q1, d0\n"
|
||||
" vmovl.s16 q2, d1\n"
|
||||
"\n"
|
||||
" # Convert to float\n"
|
||||
" vcvt.f32.s32 q1, q1\n"
|
||||
" vcvt.f32.s32 q2, q2\n"
|
||||
"\n"
|
||||
" vmul.f32 q1, q1, q8\n"
|
||||
" vmul.f32 q2, q2, q8\n"
|
||||
"\n"
|
||||
" vst1.f32 {q1-q2}, [r0]!\n"
|
||||
"\n"
|
||||
" # Guaranteed to get samples in multiples of 8.\n"
|
||||
" subs r2, r2, #8\n"
|
||||
" bne 1b\n"
|
||||
"\n"
|
||||
" bx lr\n"
|
||||
"\n"
|
||||
);
|
||||
#endif
|
||||
324
src/minarch/libretro-common/audio/dsp_filter.c
Normal file
324
src/minarch/libretro-common/audio/dsp_filter.c
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/* Copyright (C) 2010-2020 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 = (dspfilter_simd_mask_t)cpu_features_get();
|
||||
unsigned list_size = list ? (unsigned)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;
|
||||
|
||||
if (!(conf = config_file_new_from_path_to_string(filter_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;
|
||||
}
|
||||
11
src/minarch/libretro-common/audio/dsp_filters/BassBoost.dsp
Normal file
11
src/minarch/libretro-common/audio/dsp_filters/BassBoost.dsp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
filters = 2
|
||||
filter0 = iir
|
||||
filter1 = panning
|
||||
|
||||
iir_gain = 10.0
|
||||
iir_type = BBOOST
|
||||
iir_frequency = 200.0
|
||||
|
||||
# Avoids clipping.
|
||||
panning_left_mix = "0.3 0.0"
|
||||
panning_right_mix = "0.0 0.3"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
filters = 1
|
||||
filter0 = iir
|
||||
|
||||
iir_frequency = 8600.0
|
||||
iir_quality = 0.707
|
||||
iir_gain = 6.0
|
||||
iir_type = LPF
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
filters = 4
|
||||
filter0 = eq
|
||||
filter1 = reverb
|
||||
filter2 = iir
|
||||
filter3 = panning
|
||||
|
||||
eq_frequencies = "32 64 125 250 500 1000 2000 4000 8000 16000 20000"
|
||||
eq_gains = "6 9 12 7 6 5 7 9 11 6 0"
|
||||
|
||||
# Reverb - slight reverb
|
||||
reverb_drytime = 0.5
|
||||
reverb_wettime = 0.15
|
||||
reverb_damping = 0.8
|
||||
reverb_roomwidth = 0.25
|
||||
reverb_roomsize = 0.25
|
||||
|
||||
# IIR - filters out some harsh sounds on the upper end
|
||||
iir_type = RIAA_CD
|
||||
|
||||
# Panning - cut the volume a bit
|
||||
panning_left_mix = "0.75 0.0"
|
||||
panning_right_mix = "0.0 0.75"
|
||||
14
src/minarch/libretro-common/audio/dsp_filters/Chorus.dsp
Normal file
14
src/minarch/libretro-common/audio/dsp_filters/Chorus.dsp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
filters = 1
|
||||
filter0 = chorus
|
||||
|
||||
# Controls the base delay of the chorus (milliseconds).
|
||||
# chorus_delay_ms = 25.0
|
||||
#
|
||||
# Controls the depth of the delay. The delay will vary between delay_ms +/- depth_ms.
|
||||
# chorus_depth_ms = 1.0
|
||||
#
|
||||
# Frequency of LFO which controls delay.
|
||||
# chorus_lfo_freq = 0.5
|
||||
#
|
||||
# Controls dry/wet-ness of effect. 1.0 = full chorus, 0.0 = no chorus.
|
||||
# chorus_drywet = 0.8
|
||||
|
|
@ -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
src/minarch/libretro-common/audio/dsp_filters/EQ.dsp
Normal file
41
src/minarch/libretro-common/audio/dsp_filters/EQ.dsp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
filters = 1
|
||||
filter0 = eq
|
||||
|
||||
# Defaults
|
||||
|
||||
# Beta factor for Kaiser window.
|
||||
# Lower values will allow better frequency resolution, but more ripple.
|
||||
# eq_window_beta = 4.0
|
||||
|
||||
# The block size on which FFT is done.
|
||||
# Too high value requires more processing as well as longer latency but
|
||||
# allows finer-grained control over the spectrum.
|
||||
# eq_block_size_log2 = 8
|
||||
|
||||
# An array of which frequencies to control.
|
||||
# You can create an arbitrary amount of these sampling points.
|
||||
# The EQ will try to create a frequency response which fits well to these points.
|
||||
# The filter response is linearly interpolated between sampling points here.
|
||||
#
|
||||
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
|
||||
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
|
||||
#
|
||||
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
|
||||
# eq_frequencies = "500 1000 2000"
|
||||
# eq_gains = "0 3 0"
|
||||
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
|
||||
|
||||
# By default, this filter has a flat frequency response.
|
||||
|
||||
# Dumps the impulse response generated by the EQ as a plain-text file
|
||||
# with one coefficient per line.
|
||||
# eq_impulse_response_output = "eq_impulse.txt"
|
||||
#
|
||||
# Using GNU Octave or Matlab, you can plot the response with:
|
||||
#
|
||||
# f = fopen('/path/to/eq_impulse.txt');
|
||||
# l = textscan(f, '%f');
|
||||
# res = l{1};
|
||||
# freqz(res, 1, 4096, 48000);
|
||||
#
|
||||
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch
|
||||
19
src/minarch/libretro-common/audio/dsp_filters/Echo.dsp
Normal file
19
src/minarch/libretro-common/audio/dsp_filters/Echo.dsp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
filters = 1
|
||||
filter0 = echo
|
||||
|
||||
# Somewhat fancy Echo filter. Can take any number of echo channels with varying delays (ms) and feedback factors.
|
||||
# Echo output from all channels can be fed back into each other to create a somewhat reverb-like effect if desired.
|
||||
|
||||
# Defaults, 200 ms delay echo with feedback:
|
||||
# Delay in ms. Takes an array with multiple channels.
|
||||
# echo_delay = "200"
|
||||
# Feedback factor for echo.
|
||||
# echo_feedback = "0.5"
|
||||
# Overall echo amplification. If too high, the echo becomes unstable due to feedback.
|
||||
# echo_amp = "0.2"
|
||||
|
||||
# Reverby preset.
|
||||
# echo_delay = " 60 80 120 172 200 320 380"
|
||||
# echo_feedback = "0.5 0.5 0.4 0.3 0.5 0.3 0.2"
|
||||
|
||||
# echo_amp = "0.12"
|
||||
12
src/minarch/libretro-common/audio/dsp_filters/EchoReverb.dsp
Normal file
12
src/minarch/libretro-common/audio/dsp_filters/EchoReverb.dsp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
filters = 2
|
||||
filter0 = echo
|
||||
filter1 = reverb
|
||||
|
||||
echo_delay = "200"
|
||||
echo_feedback = "0.6"
|
||||
echo_amp = "0.25"
|
||||
|
||||
reverb_roomwidth = 0.75
|
||||
reverb_roomsize = 0.75
|
||||
reverb_damping = 1.0
|
||||
reverb_wettime = 0.3
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
filters = 1
|
||||
filter0 = iir
|
||||
|
||||
iir_gain = -12.0
|
||||
iir_type = HSH
|
||||
iir_frequency = 8000.0
|
||||
22
src/minarch/libretro-common/audio/dsp_filters/IIR.dsp
Normal file
22
src/minarch/libretro-common/audio/dsp_filters/IIR.dsp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
filters = 1
|
||||
filter0 = iir
|
||||
|
||||
# Defaults.
|
||||
#iir_frequency = 1024.0
|
||||
#iir_quality = 0.707
|
||||
#iir_gain = 0.0
|
||||
#iir_type = LPF
|
||||
|
||||
# Filter types:
|
||||
# LPF: Low-pass
|
||||
# HPF: High-pass
|
||||
# BPCSGF: Band-pass #1
|
||||
# BPZPGF: Band-pass #2
|
||||
# APF: Allpass
|
||||
# NOTCH: Notch filter
|
||||
# RIAA_phono: RIAA record/tape deemphasis
|
||||
# PEQ: peaking band EQ
|
||||
# BBOOST: Bassboost
|
||||
# LSH: Low-shelf
|
||||
# HSH: High-shelf
|
||||
# RIAA_CD: CD de-emphasis
|
||||
47
src/minarch/libretro-common/audio/dsp_filters/LowPassCPS.dsp
Normal file
47
src/minarch/libretro-common/audio/dsp_filters/LowPassCPS.dsp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
filters = 1
|
||||
filter0 = eq
|
||||
|
||||
eq_frequencies = "8000 10000 12500 16000 20000"
|
||||
eq_gains = "0 -30 -30 -30 -30"
|
||||
|
||||
# Low pass filter for the QSound chip from CPS-1/2.
|
||||
# Some games have aliasing due low quality samples, so you can hear some annoying noisy near 11 kHz
|
||||
|
||||
# Defaults
|
||||
|
||||
# Beta factor for Kaiser window.
|
||||
# Lower values will allow better frequency resolution, but more ripple.
|
||||
# eq_window_beta = 4.0
|
||||
|
||||
# The block size on which FFT is done.
|
||||
# Too high value requires more processing as well as longer latency but
|
||||
# allows finer-grained control over the spectrum.
|
||||
# eq_block_size_log2 = 8
|
||||
|
||||
# An array of which frequencies to control.
|
||||
# You can create an arbitrary amount of these sampling points.
|
||||
# The EQ will try to create a frequency response which fits well to these points.
|
||||
# The filter response is linearly interpolated between sampling points here.
|
||||
#
|
||||
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
|
||||
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
|
||||
#
|
||||
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
|
||||
# eq_frequencies = "500 1000 2000"
|
||||
# eq_gains = "0 3 0"
|
||||
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
|
||||
|
||||
# By default, this filter has a low pass response with cuttof frequency at ~8600 Hz.
|
||||
|
||||
# Dumps the impulse response generated by the EQ as a plain-text file
|
||||
# with one coefficient per line.
|
||||
# eq_impulse_response_output = "eq_impulse.txt"
|
||||
#
|
||||
# Using GNU Octave or Matlab, you can plot the response with:
|
||||
#
|
||||
# f = fopen('/path/to/eq_impulse.txt');
|
||||
# l = textscan(f, '%f');
|
||||
# res = l{1};
|
||||
# freqz(res, 1, 4096, 48000);
|
||||
#
|
||||
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch
|
||||
135
src/minarch/libretro-common/audio/dsp_filters/Makefile
Normal file
135
src/minarch/libretro-common/audio/dsp_filters/Makefile
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
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 -s),)
|
||||
platform = win
|
||||
else ifneq ($(findstring Darwin,$(shell uname -s)),)
|
||||
platform = osx
|
||||
arch = intel
|
||||
ifeq ($(shell uname -p),powerpc)
|
||||
arch = ppc
|
||||
endif
|
||||
else ifneq ($(findstring MINGW,$(shell uname -s)),)
|
||||
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
|
||||
ARCHFLAGS=
|
||||
MINVERFLAGS=
|
||||
ifeq ($(shell uname -p),arm)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.15 -stdlib=libc++ # macOS (Metal, ARM 64bit)
|
||||
else ifeq ($(HAVE_METAL),1)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.13 -stdlib=libc++ # macOS (Metal, x86 64bit)
|
||||
else ifeq ($(shell uname -p),powerpc)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.5 # macOSX (PowerPC 32-bit)
|
||||
else ifeq ($(shell uname -m),i386)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.6 # macOSX (OpenGL, x86 32bit)
|
||||
else
|
||||
MINVERFLAGS = -mmacosx-version-min=10.7 -stdlib=libc++ # macOSX (OpenGL, x86 64bit)
|
||||
endif
|
||||
|
||||
# Build for a specific architecture when ARCH is defined as a switch
|
||||
ifeq ($(ARCH),arm64)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.15 -stdlib=libc++ # macOS (Metal, ARM 64bit)
|
||||
ARCHFLAGS = -arch arm64
|
||||
else ifeq ($(ARCH),x86_64)
|
||||
ifeq ($(HAVE_METAL),1)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.13 -stdlib=libc++
|
||||
else
|
||||
MINVERFLAGS = -mmacosx-version-min=10.7 -stdlib=libc++
|
||||
endif
|
||||
ARCHFLAGS = -arch x86_64
|
||||
else ifeq ($(ARCH),x86)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.6
|
||||
ARCHFLAGS = -arch x86
|
||||
else ifeq ($(ARCH),ppc)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.5
|
||||
ARCHFLAGS = -arch ppc
|
||||
endif
|
||||
ifeq ($(BUILDBOT),1)
|
||||
ARCHFLAGS = -target $(LIBRETRO_APPLE_PLATFORM) -isysroot $(LIBRETRO_APPLE_ISYSROOT)
|
||||
endif
|
||||
extraflags += $(MINVERFLAGS) $(ARCHFLAGS)
|
||||
ldflags += $(MINVERFLAGS) $(ARCHFLAGS)
|
||||
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
|
||||
12
src/minarch/libretro-common/audio/dsp_filters/Mono.dsp
Normal file
12
src/minarch/libretro-common/audio/dsp_filters/Mono.dsp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
filters = 1
|
||||
filter0 = panning
|
||||
|
||||
# Gains are linear.
|
||||
|
||||
# Stereo Mono:
|
||||
panning_left_mix = "0.5 0.5"
|
||||
panning_right_mix = "0.5 0.5"
|
||||
|
||||
# Mono on one speaker:
|
||||
# panning_left_mix = "0.5 0.5"
|
||||
# panning_right_mix = "0.0 0.0"
|
||||
22
src/minarch/libretro-common/audio/dsp_filters/Panning.dsp
Normal file
22
src/minarch/libretro-common/audio/dsp_filters/Panning.dsp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
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"
|
||||
10
src/minarch/libretro-common/audio/dsp_filters/Phaser.dsp
Normal file
10
src/minarch/libretro-common/audio/dsp_filters/Phaser.dsp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
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
|
||||
9
src/minarch/libretro-common/audio/dsp_filters/Reverb.dsp
Normal file
9
src/minarch/libretro-common/audio/dsp_filters/Reverb.dsp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
filters = 1
|
||||
filter0 = reverb
|
||||
|
||||
# Defaults.
|
||||
# reverb_drytime = 0.43
|
||||
# reverb_wettime = 0.4
|
||||
# reverb_damping = 0.8
|
||||
# reverb_roomwidth = 0.56
|
||||
# reverb_roomsize = 0.56
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
filters = 1
|
||||
filter0 = tremolo
|
||||
|
||||
# Defaults.
|
||||
#tremolo_frequency = 4.0
|
||||
#tremolo_depth = 0.9
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
filters = 1
|
||||
filter0 = vibrato
|
||||
|
||||
# Defaults.
|
||||
#vibrato_frequency = 5.0
|
||||
#vibrato_depth = 0.5
|
||||
9
src/minarch/libretro-common/audio/dsp_filters/WahWah.dsp
Normal file
9
src/minarch/libretro-common/audio/dsp_filters/WahWah.dsp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
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
|
||||
160
src/minarch/libretro-common/audio/dsp_filters/chorus.c
Normal file
160
src/minarch/libretro-common/audio/dsp_filters/chorus.c
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
3
src/minarch/libretro-common/audio/dsp_filters/configure
vendored
Executable file
3
src/minarch/libretro-common/audio/dsp_filters/configure
vendored
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
PACKAGE_NAME=retroarch-filters-audio
|
||||
90
src/minarch/libretro-common/audio/dsp_filters/crystalizer.c
Normal file
90
src/minarch/libretro-common/audio/dsp_filters/crystalizer.c
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
180
src/minarch/libretro-common/audio/dsp_filters/echo.c
Normal file
180
src/minarch/libretro-common/audio/dsp_filters/echo.c
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/* Copyright (C) 2010-2020 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
src/minarch/libretro-common/audio/dsp_filters/eq.c
Normal file
352
src/minarch/libretro-common/audio/dsp_filters/eq.c
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/* Copyright (C) 2010-2020 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);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
204
src/minarch/libretro-common/audio/dsp_filters/fft/fft.c
Normal file
204
src/minarch/libretro-common/audio/dsp_filters/fft/fft.c
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/* Copyright (C) 2010-2020 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);
|
||||
}
|
||||
44
src/minarch/libretro-common/audio/dsp_filters/fft/fft.h
Normal file
44
src/minarch/libretro-common/audio/dsp_filters/fft/fft.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
371
src/minarch/libretro-common/audio/dsp_filters/iir.c
Normal file
371
src/minarch/libretro-common/audio/dsp_filters/iir.c
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/* Copyright (C) 2010-2020 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] = {0.0f};
|
||||
float a[3] = {0.0f};
|
||||
|
||||
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
src/minarch/libretro-common/audio/dsp_filters/link.T
Normal file
4
src/minarch/libretro-common/audio/dsp_filters/link.T
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
global: dspfilter_get_implementation;
|
||||
local: *;
|
||||
};
|
||||
111
src/minarch/libretro-common/audio/dsp_filters/panning.c
Normal file
111
src/minarch/libretro-common/audio/dsp_filters/panning.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
145
src/minarch/libretro-common/audio/dsp_filters/phaser.c
Normal file
145
src/minarch/libretro-common/audio/dsp_filters/phaser.c
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
318
src/minarch/libretro-common/audio/dsp_filters/reverb.c
Normal file
318
src/minarch/libretro-common/audio/dsp_filters/reverb.c
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
/* Copyright (C) 2010-2020 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[numcombs];
|
||||
float *bufallpass[numallpasses];
|
||||
|
||||
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,bool right)
|
||||
{
|
||||
|
||||
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);
|
||||
int stereosep = right ? 23 : 0;
|
||||
unsigned c;
|
||||
|
||||
|
||||
for (c = 0; c < numcombs; ++c)
|
||||
{
|
||||
rev->bufcomb[c] = malloc(r*comb_lengths[c]+stereosep*sizeof(float));
|
||||
rev->combL[c].buffer = rev->bufcomb[c];
|
||||
memset(rev->combL[c].buffer,0,r*comb_lengths[c]+stereosep*sizeof(float));
|
||||
rev->combL[c].bufsize=r*comb_lengths[c]+stereosep;
|
||||
}
|
||||
|
||||
for (c = 0; c < numallpasses; ++c)
|
||||
{
|
||||
rev->bufallpass[c] = malloc(r*allpass_lengths[c]+stereosep*sizeof(float));
|
||||
rev->allpassL[c].buffer = rev->bufallpass[c];
|
||||
memset(rev->allpassL[c].buffer,0,r*allpass_lengths[c]+stereosep*sizeof(float));
|
||||
rev->allpassL[c].bufsize=r*allpass_lengths[c]+stereosep;
|
||||
rev->allpassL[c].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]);
|
||||
}
|
||||
|
||||
for (i = 0; i < numallpasses; i++) {
|
||||
free(rev->left.bufallpass[i]);
|
||||
free(rev->right.bufallpass[i]);
|
||||
}
|
||||
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,false);
|
||||
revmodel_init(&rev->right,info->input_rate,true);
|
||||
|
||||
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
|
||||
132
src/minarch/libretro-common/audio/dsp_filters/tremolo.c
Normal file
132
src/minarch/libretro-common/audio/dsp_filters/tremolo.c
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
167
src/minarch/libretro-common/audio/dsp_filters/vibrato.c
Normal file
167
src/minarch/libretro-common/audio/dsp_filters/vibrato.c
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
147
src/minarch/libretro-common/audio/dsp_filters/wahwah.c
Normal file
147
src/minarch/libretro-common/audio/dsp_filters/wahwah.c
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
189
src/minarch/libretro-common/audio/resampler/audio_resampler.c
Normal file
189
src/minarch/libretro-common/audio/resampler/audio_resampler.c
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/* Copyright (C) 2010-2020 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 void resampler_null_process(void *a, struct resampler_data *b) { }
|
||||
static void resampler_null_free(void *a) { }
|
||||
static void *resampler_null_init(const struct resampler_config *a, double b,
|
||||
enum resampler_quality c, resampler_simd_mask_t d) { return (void*)0; }
|
||||
|
||||
retro_resampler_t null_resampler = {
|
||||
resampler_null_init,
|
||||
resampler_null_process,
|
||||
resampler_null_free,
|
||||
RESAMPLER_API_VERSION,
|
||||
"null",
|
||||
"null"
|
||||
};
|
||||
|
||||
static const retro_resampler_t *resampler_drivers[] = {
|
||||
&sinc_resampler,
|
||||
#ifdef HAVE_CC_RESAMPLER
|
||||
&CC_resampler,
|
||||
#endif
|
||||
#ifdef HAVE_NEAREST_RESAMPLER
|
||||
&nearest_resampler,
|
||||
#endif
|
||||
&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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* retro_resampler_realloc:
|
||||
* @re : Resampler handle
|
||||
* @backend : Resampler backend that is about to be set.
|
||||
* @ident : Identifier name for resampler we want.
|
||||
* @bw_ratio : Bandwidth ratio.
|
||||
*
|
||||
* Reallocates resampler. Will free previous handle before
|
||||
* allocating a new one. If ident is NULL, first resampler will be used.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
**/
|
||||
bool retro_resampler_realloc(void **re, const retro_resampler_t **backend,
|
||||
const char *ident, enum resampler_quality quality, double bw_ratio)
|
||||
{
|
||||
if (*re && *backend)
|
||||
(*backend)->free(*re);
|
||||
|
||||
*re = NULL;
|
||||
*backend = find_resampler_driver(ident);
|
||||
|
||||
if (!resampler_append_plugs(re, backend, quality, bw_ratio))
|
||||
{
|
||||
if (!*re)
|
||||
*backend = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/* Copyright (C) 2010-2020 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));
|
||||
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"
|
||||
};
|
||||
1025
src/minarch/libretro-common/audio/resampler/drivers/sinc_resampler.c
Normal file
1025
src/minarch/libretro-common/audio/resampler/drivers/sinc_resampler.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,74 @@
|
|||
/* Copyright (C) 2010-2020 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__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#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
|
||||
1745
src/minarch/libretro-common/cdrom/cdrom.c
Normal file
1745
src/minarch/libretro-common/cdrom/cdrom.c
Normal file
File diff suppressed because it is too large
Load diff
122
src/minarch/libretro-common/compat/compat_fnmatch.c
Normal file
122
src/minarch/libretro-common/compat/compat_fnmatch.c
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* Copyright (C) 2010-2020 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.
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
238
src/minarch/libretro-common/compat/compat_getopt.c
Normal file
238
src/minarch/libretro-common/compat/compat_getopt.c
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
/* Copyright (C) 2010-2020 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;
|
||||
char *save = NULL;
|
||||
char *argv0 = strdup(&argv[0][2]);
|
||||
char *token = strtok_r(argv0, "=", &save);
|
||||
const struct option *opt = NULL;
|
||||
|
||||
for (indice = 0; longopts[indice].name; indice++)
|
||||
{
|
||||
if (token && !strcmp(longopts[indice].name, token))
|
||||
{
|
||||
opt = &longopts[indice];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(argv0);
|
||||
argv0 = NULL;
|
||||
|
||||
if (!opt)
|
||||
return '?';
|
||||
|
||||
/* Handle args with '=' instead of space */
|
||||
if (opt->has_arg)
|
||||
{
|
||||
char *special_arg = strchr(argv[0], '=');
|
||||
if (special_arg)
|
||||
{
|
||||
optarg = ++special_arg;
|
||||
optind++;
|
||||
return opt->val;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 '?';
|
||||
}
|
||||
654
src/minarch/libretro-common/compat/compat_ifaddrs.c
Normal file
654
src/minarch/libretro-common/compat/compat_ifaddrs.c
Normal file
|
|
@ -0,0 +1,654 @@
|
|||
/*
|
||||
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 (;;)
|
||||
{
|
||||
int l_result;
|
||||
|
||||
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;
|
||||
|
||||
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 (;;)
|
||||
{
|
||||
int l_read;
|
||||
|
||||
free(l_buffer);
|
||||
l_buffer = malloc(l_size);
|
||||
if (!l_buffer)
|
||||
return NULL;
|
||||
|
||||
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 = (NetlinkList*)malloc(sizeof(NetlinkList));
|
||||
if (!l_item)
|
||||
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)
|
||||
{
|
||||
int l_size;
|
||||
NetlinkList *l_list = NULL;
|
||||
NetlinkList *l_end = NULL;
|
||||
int l_done = 0;
|
||||
|
||||
if (netlink_send(p_socket, p_request) < 0)
|
||||
return NULL;
|
||||
|
||||
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 = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
|
||||
if (!l_entry)
|
||||
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)
|
||||
{
|
||||
struct rtattr *l_rta;
|
||||
size_t l_rtaSize;
|
||||
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;
|
||||
|
||||
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))
|
||||
{
|
||||
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 = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
|
||||
if (!l_entry)
|
||||
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)
|
||||
{
|
||||
NetlinkList *l_linkResults;
|
||||
NetlinkList *l_addrResults;
|
||||
int l_numLinks;
|
||||
int l_socket = 0;
|
||||
int l_result = 0;
|
||||
if (!ifap)
|
||||
return -1;
|
||||
|
||||
*ifap = NULL;
|
||||
|
||||
l_socket = netlink_socket();
|
||||
|
||||
if (l_socket < 0)
|
||||
return -1;
|
||||
|
||||
l_linkResults = getResultList(l_socket, RTM_GETLINK);
|
||||
if (!l_linkResults)
|
||||
{
|
||||
close(l_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
l_addrResults = getResultList(l_socket, RTM_GETADDR);
|
||||
if (!l_addrResults)
|
||||
{
|
||||
close(l_socket);
|
||||
freeResultList(l_linkResults);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
104
src/minarch/libretro-common/compat/compat_posix_string.c
Normal file
104
src/minarch/libretro-common/compat/compat_posix_string.c
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
83
src/minarch/libretro-common/compat/compat_snprintf.c
Normal file
83
src/minarch/libretro-common/compat/compat_snprintf.c
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/* Copyright (C) 2010-2020 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 <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if _MSC_VER < 1800
|
||||
#define va_copy(dst, src) ((dst) = (src))
|
||||
#endif
|
||||
|
||||
#if _MSC_VER < 1300
|
||||
#define _vscprintf c89_vscprintf_retro__
|
||||
|
||||
static int c89_vscprintf_retro__(const char *fmt, va_list pargs)
|
||||
{
|
||||
int retval;
|
||||
va_list argcopy;
|
||||
va_copy(argcopy, pargs);
|
||||
retval = vsnprintf(NULL, 0, fmt, argcopy);
|
||||
va_end(argcopy);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */
|
||||
|
||||
int c99_vsnprintf_retro__(char *s, size_t len, const char *fmt, va_list ap)
|
||||
{
|
||||
int count = -1;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
#if (_MSC_VER <= 1310)
|
||||
count = _vsnprintf(s, len - 1, fmt, ap);
|
||||
#else
|
||||
count = _vsnprintf_s(s, len, len - 1, fmt, ap);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (count == -1)
|
||||
count = _vscprintf(fmt, ap);
|
||||
|
||||
/* there was no room for a NULL, so truncate the last character */
|
||||
if (count == len && len)
|
||||
s[len - 1] = '\0';
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int c99_snprintf_retro__(char *s, size_t len, const char *fmt, ...)
|
||||
{
|
||||
int count;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
count = c99_vsnprintf_retro__(s, len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
58
src/minarch/libretro-common/compat/compat_strcasestr.c
Normal file
58
src/minarch/libretro-common/compat/compat_strcasestr.c
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* Copyright (C) 2010-2020 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;
|
||||
}
|
||||
62
src/minarch/libretro-common/compat/compat_strl.c
Normal file
62
src/minarch/libretro-common/compat/compat_strl.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
33
src/minarch/libretro-common/compat/compat_strldup.c
Normal file
33
src/minarch/libretro-common/compat/compat_strldup.c
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* Copyright (C) 2010-2020 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>
|
||||
|
||||
char *strldup(const char *s, size_t n)
|
||||
{
|
||||
char *dst = (char*)malloc(sizeof(char) * (n + 1));
|
||||
strlcpy(dst, s, n);
|
||||
return dst;
|
||||
}
|
||||
44
src/minarch/libretro-common/compat/compat_vscprintf.c
Normal file
44
src/minarch/libretro-common/compat/compat_vscprintf.c
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
64
src/minarch/libretro-common/compat/fopen_utf8.c
Normal file
64
src/minarch/libretro-common/compat/fopen_utf8.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* Copyright (C) 2010-2020 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(LEGACY_WIN32)
|
||||
char * filename_local = utf8_to_local_string_alloc(filename);
|
||||
if (filename_local)
|
||||
{
|
||||
FILE *ret = fopen(filename_local, mode);
|
||||
free(filename_local);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
|
||||
if (filename_w)
|
||||
{
|
||||
FILE *ret = NULL;
|
||||
wchar_t *mode_w = utf8_to_utf16_string_alloc(mode);
|
||||
if (mode_w)
|
||||
{
|
||||
ret = _wfopen(filename_w, mode_w);
|
||||
free(mode_w);
|
||||
}
|
||||
free(filename_w);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
10
src/minarch/libretro-common/crt/include/string.h
Normal file
10
src/minarch/libretro-common/crt/include/string.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef __LIBRETRO_SDK_CRT_STRING_H_
|
||||
#define __LIBRETRO_SDK_CRT_STRING_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void *memcpy(void *dst, const void *src, size_t len);
|
||||
|
||||
void *memset(void *b, int c, size_t len);
|
||||
|
||||
#endif
|
||||
34
src/minarch/libretro-common/crt/string.c
Normal file
34
src/minarch/libretro-common/crt/string.c
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifdef _MSC_VER
|
||||
#include <cruntime.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void *memset(void *dst, int val, size_t count)
|
||||
{
|
||||
void *start = dst;
|
||||
|
||||
#if defined(_M_IA64) || defined (_M_AMD64) || defined(_M_ALPHA) || defined (_M_PPC)
|
||||
extern void RtlFillMemory(void *, size_t count, char);
|
||||
|
||||
RtlFillMemory(dst, count, (char)val);
|
||||
#else
|
||||
while (count--)
|
||||
{
|
||||
*(char*)dst = (char)val;
|
||||
dst = (char*)dst + 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
void *memcpy(void *dst, const void *src, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
((unsigned char *)dst)[i] = ((unsigned char *)src)[i];
|
||||
|
||||
return dst;
|
||||
}
|
||||
225
src/minarch/libretro-common/dynamic/dylib.c
Normal file
225
src/minarch/libretro-common/dynamic/dylib.c
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/* Copyright (C) 2010-2020 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>
|
||||
|
||||
#if defined(ORBIS)
|
||||
#include <orbis/libkernel.h>
|
||||
#endif
|
||||
|
||||
#ifdef NEED_DYNAMIC
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <compat/posix_string.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#if !defined(ORBIS)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#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.
|
||||
*
|
||||
* @return Library handle on success, otherwise NULL.
|
||||
**/
|
||||
dylib_t dylib_load(const char *path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifndef __WINRT__
|
||||
int prevmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
||||
#endif
|
||||
#ifdef __WINRT__
|
||||
dylib_t lib;
|
||||
/* On UWP, you can only load DLLs inside your install directory, using a special function that takes a relative path */
|
||||
char relative_path_abbrev[PATH_MAX_LENGTH];
|
||||
char *relative_path = relative_path_abbrev;
|
||||
wchar_t *path_wide = NULL;
|
||||
|
||||
relative_path_abbrev[0] = '\0';
|
||||
|
||||
if (!path_is_absolute(path))
|
||||
RARCH_WARN("Relative path in dylib_load! This is likely an attempt to load a system library that will fail\n");
|
||||
|
||||
fill_pathname_abbreviate_special(relative_path_abbrev, path, sizeof(relative_path_abbrev));
|
||||
|
||||
/* Path to dylib_load is not inside app install directory.
|
||||
* Loading will probably fail. */
|
||||
if (relative_path[0] != ':' || !PATH_CHAR_IS_SLASH(relative_path[1])) { }
|
||||
else
|
||||
relative_path += 2;
|
||||
|
||||
path_wide = utf8_to_utf16_string_alloc(relative_path);
|
||||
lib = LoadPackagedLibrary(path_wide, 0);
|
||||
free(path_wide);
|
||||
#elif defined(LEGACY_WIN32)
|
||||
dylib_t lib = LoadLibrary(path);
|
||||
#else
|
||||
wchar_t *path_wide = utf8_to_utf16_string_alloc(path);
|
||||
dylib_t lib = LoadLibraryW(path_wide);
|
||||
free(path_wide);
|
||||
#endif
|
||||
|
||||
#ifndef __WINRT__
|
||||
SetErrorMode(prevmode);
|
||||
#endif
|
||||
|
||||
if (!lib)
|
||||
{
|
||||
set_dl_error();
|
||||
return NULL;
|
||||
}
|
||||
last_dyn_error[0] = 0;
|
||||
#elif defined(ORBIS)
|
||||
int res;
|
||||
dylib_t lib = (dylib_t)sceKernelLoadStartModule(path, 0, NULL, 0, NULL, &res);
|
||||
#else
|
||||
dylib_t lib = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
|
||||
#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
|
||||
HMODULE mod = (HMODULE)lib;
|
||||
if (!mod)
|
||||
{
|
||||
#ifdef __WINRT__
|
||||
/* GetModuleHandle is not available on UWP */
|
||||
/* It's not possible to lookup symbols in current executable
|
||||
* on UWP. */
|
||||
DebugBreak();
|
||||
return NULL;
|
||||
#else
|
||||
mod = GetModuleHandle(NULL);
|
||||
#endif
|
||||
}
|
||||
if (!(sym = (function_t)GetProcAddress(mod, proc)))
|
||||
{
|
||||
set_dl_error();
|
||||
return NULL;
|
||||
}
|
||||
last_dyn_error[0] = 0;
|
||||
#elif defined(ORBIS)
|
||||
void *ptr_sym = NULL;
|
||||
sym = NULL;
|
||||
|
||||
if (lib)
|
||||
{
|
||||
sceKernelDlsym((SceKernelModule)lib, proc, &ptr_sym);
|
||||
memcpy(&sym, &ptr_sym, sizeof(void*));
|
||||
}
|
||||
#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;
|
||||
#elif defined(ORBIS)
|
||||
int res;
|
||||
sceKernelStopUnloadModule((SceKernelModule)lib, 0, NULL, 0, NULL, &res);
|
||||
#else
|
||||
#ifndef NO_DLCLOSE
|
||||
dlclose(lib);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
183
src/minarch/libretro-common/encodings/encoding_base64.c
Normal file
183
src/minarch/libretro-common/encodings/encoding_base64.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
https://github.com/superwills/NibbleAndAHalf
|
||||
base64.h -- Fast base64 encoding and decoding.
|
||||
version 1.0.0, April 17, 2013 143a
|
||||
Copyright (C) 2013 William Sherif
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
William Sherif
|
||||
will.sherif@gmail.com
|
||||
YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
|
||||
|
||||
|
||||
Modified for RetroArch formatting, logging, and header files.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <encodings/base64.h>
|
||||
|
||||
const static char* b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/* maps A=>0,B=>1.. */
|
||||
const static unsigned char unb64[]={
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 62, 0, 0, 0, 63, 52, 53,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 2, 3, 4,
|
||||
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 0, 0, 0, 0, 0, 0, 26, 27, 28,
|
||||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
}; /* This array has 256 elements */
|
||||
|
||||
/*
|
||||
Converts binary data of length=len to base64 characters.
|
||||
Length of the resultant string is stored in flen
|
||||
(you must pass pointer flen).
|
||||
*/
|
||||
char* base64(const void* binaryData, int len, int *flen)
|
||||
{
|
||||
const unsigned char* bin = (const unsigned char*) binaryData;
|
||||
char* res;
|
||||
|
||||
int rc = 0; /* result counter */
|
||||
int byteNo; /* I need this after the loop */
|
||||
|
||||
int modulusLen = len % 3 ;
|
||||
|
||||
/* 2 gives 1 and 1 gives 2, but 0 gives 0. */
|
||||
int pad = ((modulusLen&1)<<1) + ((modulusLen&2)>>1);
|
||||
|
||||
*flen = 4*(len + pad)/3;
|
||||
res = (char*) malloc(*flen + 1); /* and one for the null */
|
||||
if (!res)
|
||||
{
|
||||
/* ERROR: base64 could not allocate enough memory. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (byteNo=0; byteNo <= len-3; byteNo+=3)
|
||||
{
|
||||
unsigned char BYTE0 = bin[byteNo];
|
||||
unsigned char BYTE1 = bin[byteNo+1];
|
||||
unsigned char BYTE2 = bin[byteNo+2];
|
||||
|
||||
res[rc++] = b64[BYTE0 >> 2];
|
||||
res[rc++] = b64[((0x3&BYTE0)<<4) + (BYTE1 >> 4)];
|
||||
res[rc++] = b64[((0x0f&BYTE1)<<2) + (BYTE2>>6)];
|
||||
res[rc++] = b64[0x3f&BYTE2];
|
||||
}
|
||||
|
||||
if (pad==2)
|
||||
{
|
||||
res[rc++] = b64[bin[byteNo] >> 2];
|
||||
res[rc++] = b64[(0x3&bin[byteNo])<<4];
|
||||
res[rc++] = '=';
|
||||
res[rc++] = '=';
|
||||
}
|
||||
else if (pad==1)
|
||||
{
|
||||
res[rc++] = b64[bin[byteNo] >> 2];
|
||||
res[rc++] = b64[((0x3&bin[byteNo])<<4) + (bin[byteNo+1] >> 4)];
|
||||
res[rc++] = b64[(0x0f&bin[byteNo+1])<<2];
|
||||
res[rc++] = '=';
|
||||
}
|
||||
|
||||
res[rc]=0; /* NULL TERMINATOR! ;) */
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned char* unbase64(const char* ascii, int len, int *flen)
|
||||
{
|
||||
const unsigned char *safeAsciiPtr = (const unsigned char*) ascii;
|
||||
unsigned char *bin;
|
||||
int cb = 0;
|
||||
int charNo;
|
||||
int pad = 0;
|
||||
|
||||
if (len < 2) { /* 2 accesses below would be OOB. */
|
||||
/* catch empty string, return NULL as result. */
|
||||
|
||||
/* ERROR: You passed an invalid base64 string (too short).
|
||||
* You get NULL back. */
|
||||
*flen = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(safeAsciiPtr[len-1]=='=')
|
||||
++pad;
|
||||
if(safeAsciiPtr[len-2]=='=')
|
||||
++pad;
|
||||
|
||||
*flen = 3*len/4 - pad;
|
||||
bin = (unsigned char*)malloc(*flen);
|
||||
|
||||
if (!bin)
|
||||
{
|
||||
/* ERROR: unbase64 could not allocate enough memory. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (charNo=0; charNo <= len-4-pad; charNo+=4)
|
||||
{
|
||||
int A = unb64[safeAsciiPtr[charNo]];
|
||||
int B = unb64[safeAsciiPtr[charNo+1]];
|
||||
int C = unb64[safeAsciiPtr[charNo+2]];
|
||||
int D = unb64[safeAsciiPtr[charNo+3]];
|
||||
|
||||
bin[cb++] = (A<<2) | (B>>4);
|
||||
bin[cb++] = (B<<4) | (C>>2);
|
||||
bin[cb++] = (C<<6) | (D);
|
||||
}
|
||||
|
||||
if (pad==1)
|
||||
{
|
||||
int A = unb64[safeAsciiPtr[charNo]];
|
||||
int B = unb64[safeAsciiPtr[charNo+1]];
|
||||
int C = unb64[safeAsciiPtr[charNo+2]];
|
||||
|
||||
bin[cb++] = (A<<2) | (B>>4);
|
||||
bin[cb++] = (B<<4) | (C>>2);
|
||||
}
|
||||
else if (pad==2)
|
||||
{
|
||||
int A = unb64[safeAsciiPtr[charNo]];
|
||||
int B = unb64[safeAsciiPtr[charNo+1]];
|
||||
|
||||
bin[cb++] = (A<<2) | (B>>4);
|
||||
}
|
||||
|
||||
return bin;
|
||||
}
|
||||
|
||||
91
src/minarch/libretro-common/encodings/encoding_crc32.c
Normal file
91
src/minarch/libretro-common/encodings/encoding_crc32.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/* Copyright (C) 2010-2020 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>
|
||||
#include <stdlib.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;
|
||||
}
|
||||
532
src/minarch/libretro-common/encodings/encoding_utf.c
Normal file
532
src/minarch/libretro-common/encodings/encoding_utf.c
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
|
||||
#define UTF8_WALKBYTE(string) (*((*(string))++))
|
||||
|
||||
static unsigned leading_ones(uint8_t c)
|
||||
{
|
||||
unsigned ones = 0;
|
||||
while (c & 0x80)
|
||||
{
|
||||
ones++;
|
||||
c <<= 1;
|
||||
}
|
||||
|
||||
return ones;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8_conv_utf32:
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf16_conv_utf8:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||
const uint16_t *in, size_t in_size)
|
||||
{
|
||||
size_t out_pos = 0;
|
||||
size_t in_pos = 0;
|
||||
static const
|
||||
uint8_t utf8_limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unsigned num_adds;
|
||||
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 (num_adds = 1; num_adds < 5; num_adds++)
|
||||
if (value < (((uint32_t)1) << (num_adds * 5 + 6)))
|
||||
break;
|
||||
if (out)
|
||||
out[out_pos] = (char)(utf8_limits[num_adds - 1]
|
||||
+ (value >> (6 * num_adds)));
|
||||
out_pos++;
|
||||
do
|
||||
{
|
||||
num_adds--;
|
||||
if (out)
|
||||
out[out_pos] = (char)(0x80
|
||||
+ ((value >> (6 * num_adds)) & 0x3F));
|
||||
out_pos++;
|
||||
}while (num_adds != 0);
|
||||
}
|
||||
|
||||
*out_chars = out_pos;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8cpy:
|
||||
*
|
||||
* 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.
|
||||
* @s is assumed valid UTF-8.
|
||||
* Use only if @chars is considerably less than @d_len.
|
||||
*
|
||||
* @return Number of bytes.
|
||||
**/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8skip:
|
||||
*
|
||||
* Leaf function
|
||||
**/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8len:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
size_t utf8len(const char *string)
|
||||
{
|
||||
size_t ret = 0;
|
||||
|
||||
if (!string)
|
||||
return 0;
|
||||
|
||||
while (*string)
|
||||
{
|
||||
if ((*string & 0xC0) != 0x80)
|
||||
ret++;
|
||||
string++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8_walk:
|
||||
*
|
||||
* Does not validate the input.
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return 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);
|
||||
return ret | (first & 7) << 18;
|
||||
}
|
||||
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;
|
||||
if ((*utf_data = (uint8_t*)malloc(*dest_len)) != 0)
|
||||
return utf16_conv_utf8(*utf_data, dest_len, in, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf16_to_char_string:
|
||||
**/
|
||||
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;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
|
||||
/**
|
||||
* mb_to_mb_string_alloc:
|
||||
*
|
||||
* @return 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)
|
||||
{
|
||||
wchar_t *path_buf_wide = NULL;
|
||||
int path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
if (!path_buf_wide_len)
|
||||
return strdup(str);
|
||||
|
||||
if ((path_buf_wide = (wchar_t*)
|
||||
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t))))
|
||||
{
|
||||
MultiByteToWideChar(cp_in, 0,
|
||||
str, -1, path_buf_wide, path_buf_wide_len);
|
||||
|
||||
if (*path_buf_wide)
|
||||
{
|
||||
int path_buf_len = WideCharToMultiByte(cp_out, 0,
|
||||
path_buf_wide, -1, NULL, 0, NULL, NULL);
|
||||
|
||||
if (path_buf_len)
|
||||
{
|
||||
char *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);
|
||||
}
|
||||
}
|
||||
|
||||
free(path_buf_wide);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* utf8_to_local_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char* utf8_to_local_string_alloc(const char *str)
|
||||
{
|
||||
if (str && *str)
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
|
||||
return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
|
||||
#else
|
||||
return strdup(str); /* Assume string needs no modification if not on Windows */
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* local_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *local_to_utf8_string_alloc(const char *str)
|
||||
{
|
||||
if (str && *str)
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
|
||||
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
|
||||
#else
|
||||
return strdup(str); /* Assume string needs no modification if not on Windows */
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8_to_utf16_string_alloc:
|
||||
*
|
||||
* @return 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;
|
||||
#else
|
||||
size_t len = 0;
|
||||
#endif
|
||||
wchar_t *buf = NULL;
|
||||
|
||||
if (!str || !*str)
|
||||
return NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
if ((len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)))
|
||||
{
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
if ((MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len)) < 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fallback to ANSI codepage instead */
|
||||
if ((len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0)))
|
||||
{
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
if ((MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len)) < 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
|
||||
if ((len = mbstowcs(NULL, str, 0) + 1))
|
||||
{
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
if ((mbstowcs(buf, str, len)) == (size_t)-1)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf16_to_utf8_string_alloc:
|
||||
*
|
||||
* @return 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;
|
||||
#else
|
||||
size_t len = 0;
|
||||
#endif
|
||||
char *buf = NULL;
|
||||
|
||||
if (!str || !*str)
|
||||
return NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
{
|
||||
UINT code_page = CP_UTF8;
|
||||
|
||||
/* fallback to ANSI codepage instead */
|
||||
if (!(len = WideCharToMultiByte(code_page,
|
||||
0, str, -1, NULL, 0, NULL, NULL)))
|
||||
{
|
||||
code_page = CP_ACP;
|
||||
len = WideCharToMultiByte(code_page,
|
||||
0, str, -1, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
if (!(buf = (char*)calloc(len, sizeof(char))))
|
||||
return NULL;
|
||||
|
||||
if (WideCharToMultiByte(code_page,
|
||||
0, str, -1, buf, len, NULL, NULL) < 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* NOTE: For now, assume non-Windows platforms'
|
||||
* locale is already UTF-8. */
|
||||
if ((len = wcstombs(NULL, str, 0) + 1))
|
||||
{
|
||||
if (!(buf = (char*)calloc(len, sizeof(char))))
|
||||
return NULL;
|
||||
|
||||
if (wcstombs(buf, str, len) == (size_t)-1)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return buf;
|
||||
}
|
||||
919
src/minarch/libretro-common/features/features_cpu.c
Normal file
919
src/minarch/libretro-common/features/features_cpu.c
Normal file
|
|
@ -0,0 +1,919 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
|
||||
#ifdef __PSL1GHT__
|
||||
#include <lv2/systime.h>
|
||||
#endif
|
||||
|
||||
#if defined(_XBOX360)
|
||||
#include <PPCIntrinsics.h>
|
||||
#elif !defined(__MACH__) && (defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC64__) || defined(__powerpc64__))
|
||||
#ifndef _PPU_INTRINSICS_H
|
||||
#include <ppu_intrinsics.h>
|
||||
#endif
|
||||
#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>
|
||||
#endif
|
||||
|
||||
#if defined(PSP) || defined(__PSL1GHT__)
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#if defined(PSP)
|
||||
#include <psprtc.h>
|
||||
#endif
|
||||
|
||||
#if defined(VITA)
|
||||
#include <psp2/kernel/processmgr.h>
|
||||
#include <psp2/rtc.h>
|
||||
#endif
|
||||
|
||||
#if defined(ORBIS)
|
||||
#include <orbis/libkernel.h>
|
||||
#endif
|
||||
|
||||
#if defined(PS2)
|
||||
#include <ps2sdkapi.h>
|
||||
#endif
|
||||
|
||||
#if !defined(__PSL1GHT__) && defined(__PS3__)
|
||||
#include <sys/sys_time.h>
|
||||
#endif
|
||||
|
||||
#ifdef GEKKO
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIIU
|
||||
#include <wiiu/os/time.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBNX)
|
||||
#include <switch.h>
|
||||
#elif defined(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
|
||||
|
||||
/**
|
||||
* TODO/FIXME: clock_gettime 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.
|
||||
*
|
||||
* @return 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(GEKKO)
|
||||
time_ticks = gettime();
|
||||
#elif !defined(__MACH__) && (defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__) || defined(__PSL1GHT__) || defined(__PPC64__) || defined(__powerpc64__))
|
||||
time_ticks = __mftb();
|
||||
#elif (defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0) || defined(__QNX__) || defined(ANDROID)
|
||||
struct timespec tv;
|
||||
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__) || defined(_M_X64) || defined(_M_AMD64)
|
||||
__asm__ volatile ("rdtsc" : "=A" (time_ticks));
|
||||
#elif defined(__GNUC__) && defined(__x86_64__) || defined(_M_IX86)
|
||||
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(__aarch64__)
|
||||
__asm__ volatile( "mrs %0, cntvct_el0" : "=r"(time_ticks) );
|
||||
#elif defined(PSP) || defined(VITA)
|
||||
time_ticks = sceKernelGetSystemTimeWide();
|
||||
#elif defined(ORBIS)
|
||||
sceRtcGetCurrentTick((SceRtcTick*)&time_ticks);
|
||||
#elif defined(PS2)
|
||||
time_ticks = ps2_clock();
|
||||
#elif defined(_3DS)
|
||||
time_ticks = svcGetSystemTick();
|
||||
#elif defined(WIIU)
|
||||
time_ticks = OSGetSystemTime();
|
||||
#elif defined(HAVE_LIBNX)
|
||||
time_ticks = armGetSystemTick();
|
||||
#elif defined(EMSCRIPTEN)
|
||||
time_ticks = emscripten_get_now() * 1000;
|
||||
#endif
|
||||
|
||||
return time_ticks;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpu_features_get_time_usec:
|
||||
*
|
||||
* Gets time in microseconds.
|
||||
*
|
||||
* @return 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 / freq.QuadPart * 1000000) + (count.QuadPart % freq.QuadPart * 1000000 / freq.QuadPart);
|
||||
#elif defined(__PSL1GHT__)
|
||||
return sysGetSystemTime();
|
||||
#elif !defined(__PSL1GHT__) && defined(__PS3__)
|
||||
return sys_time_get_system_time();
|
||||
#elif defined(GEKKO)
|
||||
return ticks_to_microsecs(gettime());
|
||||
#elif defined(WIIU)
|
||||
return ticks_to_us(OSGetSystemTime());
|
||||
#elif defined(SWITCH) || defined(HAVE_LIBNX)
|
||||
return (svcGetSystemTick() * 10) / 192;
|
||||
#elif defined(_3DS)
|
||||
return osGetTime() * 1000;
|
||||
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__)
|
||||
struct timespec tv;
|
||||
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(PS2)
|
||||
return ps2_clock() / PS2_CLOCKS_PER_MSEC * 1000;
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
return sceKernelGetSystemTimeWide();
|
||||
#elif defined(DJGPP)
|
||||
return uclock() * 1000000LL / UCLOCKS_PER_SEC;
|
||||
#elif defined(ORBIS)
|
||||
return sceKernelGetProcessTime();
|
||||
#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
|
||||
#ifndef NDEBUG
|
||||
printf("Unknown compiler. Cannot check CPUID with inline assembly.\n");
|
||||
#endif
|
||||
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
|
||||
#ifndef NDEBUG
|
||||
printf("Unknown compiler. Cannot check xgetbv bits.\n");
|
||||
#endif
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_NEON__)
|
||||
#if defined(__arm__)
|
||||
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
|
||||
#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)))
|
||||
{
|
||||
if (strncmp(line, "Features\t: ", 11))
|
||||
continue;
|
||||
|
||||
if (strstr(line + 11, feature))
|
||||
status = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
filestream_close(fp);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#if !defined(_SC_NPROCESSORS_ONLN)
|
||||
/**
|
||||
* parse_decimal:
|
||||
*
|
||||
* 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').
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpulist_parse:
|
||||
* 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 */
|
||||
if (!(p = parse_decimal(p, q, &start_value)))
|
||||
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 == '-')
|
||||
{
|
||||
if (!(p = parse_decimal(p+1, q, &end_value)))
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set bits CPU list bits */
|
||||
for (val = start_value; val <= end_value; val++)
|
||||
{
|
||||
if ((unsigned)val < 32)
|
||||
list->mask |= (uint32_t)(UINT32_C(1) << val);
|
||||
}
|
||||
|
||||
/* Jump to next item */
|
||||
p = q;
|
||||
if (p < end)
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cpulist_read_from:
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @return Amount of CPU cores available.
|
||||
**/
|
||||
unsigned cpu_features_get_core_amount(void)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(_XBOX)
|
||||
/* Win32 */
|
||||
SYSTEM_INFO sysinfo;
|
||||
#if defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
|
||||
GetNativeSystemInfo(&sysinfo);
|
||||
#else
|
||||
GetSystemInfo(&sysinfo);
|
||||
#endif
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
#elif defined(GEKKO)
|
||||
return 1;
|
||||
#elif defined(PSP) || defined(PS2)
|
||||
return 1;
|
||||
#elif defined(__PSL1GHT__) || !defined(__PSL1GHT__) && defined(__PS3__)
|
||||
return 1; /* Only one PPU, SPUs don't really count */
|
||||
#elif defined(VITA)
|
||||
return 4;
|
||||
#elif defined(HAVE_LIBNX) || defined(SWITCH)
|
||||
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..
|
||||
*
|
||||
* @return Bitmask of all CPU features available.
|
||||
**/
|
||||
uint64_t cpu_features_get(void)
|
||||
{
|
||||
uint64_t cpu = 0;
|
||||
#if defined(CPU_X86) && !defined(__MACH__)
|
||||
int vendor_is_intel = 0;
|
||||
const int avx_flags = (1 << 27) | (1 << 28);
|
||||
#endif
|
||||
#if defined(__MACH__)
|
||||
size_t len = sizeof(size_t);
|
||||
|
||||
if (sysctlbyname("hw.optional.floatingpoint", NULL, &len, NULL, 0) == 0)
|
||||
cpu |= RETRO_SIMD_CMOV;
|
||||
|
||||
#if defined(CPU_X86)
|
||||
len = sizeof(size_t);
|
||||
if (sysctlbyname("hw.optional.mmx", NULL, &len, NULL, 0) == 0)
|
||||
cpu |= RETRO_SIMD_MMX | RETRO_SIMD_MMXEXT;
|
||||
|
||||
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;
|
||||
|
||||
#else
|
||||
len = sizeof(size_t);
|
||||
if (sysctlbyname("hw.optional.neon", NULL, &len, NULL, 0) == 0)
|
||||
cpu |= RETRO_SIMD_NEON;
|
||||
|
||||
len = sizeof(size_t);
|
||||
if (sysctlbyname("hw.optional.neon_fp16", NULL, &len, NULL, 0) == 0)
|
||||
cpu |= RETRO_SIMD_VFPV3;
|
||||
|
||||
len = sizeof(size_t);
|
||||
if (sysctlbyname("hw.optional.neon_hpfp", NULL, &len, NULL, 0) == 0)
|
||||
cpu |= RETRO_SIMD_VFPV4;
|
||||
#endif
|
||||
#elif defined(_XBOX1)
|
||||
cpu |= RETRO_SIMD_MMX | RETRO_SIMD_SSE | RETRO_SIMD_MMXEXT;
|
||||
#elif defined(CPU_X86)
|
||||
unsigned max_flag = 0;
|
||||
int flags[4];
|
||||
int vendor_shuffle[3];
|
||||
char vendor[13];
|
||||
uint64_t cpu_flags = 0;
|
||||
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;
|
||||
|
||||
/* SSE also implies MMXEXT (according to FFmpeg source). */
|
||||
if (flags[3] & (1 << 25))
|
||||
cpu |= RETRO_SIMD_SSE | 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;
|
||||
#if defined(__ARM_NEON__) && defined(__arm__)
|
||||
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;
|
||||
#if defined(__arm__)
|
||||
arm_enable_runfast_mode();
|
||||
#endif
|
||||
#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;
|
||||
#if defined(__arm__)
|
||||
arm_enable_runfast_mode();
|
||||
#endif
|
||||
#elif defined(__ALTIVEC__)
|
||||
cpu |= RETRO_SIMD_VMX;
|
||||
#elif defined(XBOX360)
|
||||
cpu |= RETRO_SIMD_VMX128;
|
||||
#elif defined(PSP) || defined(PS2)
|
||||
cpu |= RETRO_SIMD_VFPU;
|
||||
#elif defined(GEKKO)
|
||||
cpu |= RETRO_SIMD_PS;
|
||||
#endif
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
void cpu_features_get_model_name(char *name, int len)
|
||||
{
|
||||
#if defined(CPU_X86) && !defined(__MACH__)
|
||||
union {
|
||||
int i[4];
|
||||
unsigned char s[16];
|
||||
} flags;
|
||||
int i, j;
|
||||
size_t pos = 0;
|
||||
bool start = false;
|
||||
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
x86_cpuid(0x80000000, flags.i);
|
||||
|
||||
if (flags.i[0] < 0x80000004)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
memset(flags.i, 0, sizeof(flags.i));
|
||||
x86_cpuid(0x80000002 + i, flags.i);
|
||||
|
||||
for (j = 0; j < sizeof(flags.s); j++)
|
||||
{
|
||||
if (!start && flags.s[j] == ' ')
|
||||
continue;
|
||||
else
|
||||
start = true;
|
||||
|
||||
if (pos == len - 1)
|
||||
{
|
||||
/* truncate if we ran out of room */
|
||||
name[pos] = '\0';
|
||||
goto end;
|
||||
}
|
||||
|
||||
name[pos++] = flags.s[j];
|
||||
}
|
||||
}
|
||||
end:
|
||||
/* terminate our string */
|
||||
if (pos < (size_t)len)
|
||||
name[pos] = '\0';
|
||||
#elif defined(__MACH__)
|
||||
if (!name)
|
||||
return;
|
||||
{
|
||||
size_t len_size = len;
|
||||
sysctlbyname("machdep.cpu.brand_string", name, &len_size, NULL, 0);
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
if (!name)
|
||||
return;
|
||||
{
|
||||
char *model_name, line[128];
|
||||
RFILE *fp = filestream_open("/proc/cpuinfo",
|
||||
RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
while (filestream_gets(fp, line, sizeof(line)))
|
||||
{
|
||||
if (strncmp(line, "model name", 10))
|
||||
continue;
|
||||
|
||||
if ((model_name = strstr(line + 10, ": ")))
|
||||
{
|
||||
model_name += 2;
|
||||
strncpy(name, model_name, len);
|
||||
name[len - 1] = '\0';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
filestream_close(fp);
|
||||
}
|
||||
#else
|
||||
if (!name)
|
||||
return;
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
734
src/minarch/libretro-common/file/archive_file.c
Normal file
734
src/minarch/libretro-common/file/archive_file.c
Normal file
|
|
@ -0,0 +1,734 @@
|
|||
/* Copyright (C) 2010-2020 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>
|
||||
|
||||
#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>
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#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;
|
||||
attr.i = 0;
|
||||
|
||||
if (valid_exts)
|
||||
{
|
||||
size_t path_len = strlen(path);
|
||||
/* Checks if this entry is a directory or a file. */
|
||||
char last_char = path[path_len - 1];
|
||||
struct string_list ext_list = {0};
|
||||
|
||||
/* Skip if directory. */
|
||||
if (last_char == '/' || last_char == '\\' )
|
||||
return 1;
|
||||
|
||||
string_list_initialize(&ext_list);
|
||||
if (string_split_noalloc(&ext_list, valid_exts, "|"))
|
||||
{
|
||||
const char *file_ext = path_get_extension(path);
|
||||
|
||||
if (!file_ext)
|
||||
{
|
||||
string_list_deinitialize(&ext_list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!string_list_find_elem_prefix(&ext_list, ".", file_ext))
|
||||
{
|
||||
/* keep iterating */
|
||||
string_list_deinitialize(&ext_list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE;
|
||||
}
|
||||
|
||||
string_list_deinitialize(&ext_list);
|
||||
}
|
||||
|
||||
return string_list_append(userdata->list, path, attr);
|
||||
}
|
||||
|
||||
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];
|
||||
const char *delim;
|
||||
|
||||
if ((delim = path_get_archive_delim(userdata->archive_path)))
|
||||
{
|
||||
if (!string_is_equal_noncase(
|
||||
userdata->current_file_path, delim + 1))
|
||||
return 1; /* keep searching for the right file */
|
||||
}
|
||||
|
||||
if (userdata->extraction_directory)
|
||||
fill_pathname_join_special(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));
|
||||
|
||||
if (file_archive_perform_mode(new_path,
|
||||
valid_exts, cdata, cmode, csize, size,
|
||||
checksum, userdata))
|
||||
{
|
||||
userdata->found_file = true;
|
||||
userdata->first_extracted_file_path = strdup(new_path);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
strlcpy(path, file, sizeof(path));
|
||||
|
||||
if ((last = (char*)path_get_archive_delim(path)))
|
||||
*last = '\0';
|
||||
|
||||
if (!(state->backend = file_archive_get_file_backend(path)))
|
||||
return -1;
|
||||
|
||||
/* Failed to open archive. */
|
||||
if (!(state->archive_file = filestream_open(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
return -1;
|
||||
|
||||
state->archive_size = filestream_get_size(state->archive_file);
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
if (state->archive_size <= (256*1024*1024))
|
||||
{
|
||||
state->archive_mmap_fd = open(path, O_RDONLY);
|
||||
if (state->archive_mmap_fd)
|
||||
{
|
||||
state->archive_mmap_data = (uint8_t*)mmap(NULL, (size_t)state->archive_size,
|
||||
PROT_READ, MAP_SHARED, state->archive_mmap_fd, 0);
|
||||
|
||||
if (state->archive_mmap_data == (uint8_t*)MAP_FAILED)
|
||||
{
|
||||
close(state->archive_mmap_fd);
|
||||
state->archive_mmap_fd = 0;
|
||||
state->archive_mmap_data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
state->step_current = 0;
|
||||
state->step_total = 0;
|
||||
|
||||
return state->backend->archive_parse_file_init(state, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* file_archive_decompress_data_to_file:
|
||||
* @path : filename path of archive.
|
||||
* @size : output file size
|
||||
* @checksum : CRC32 checksum from input data.
|
||||
*
|
||||
* Write data to file.
|
||||
*
|
||||
* Returns: true (1) on success, otherwise false (0).
|
||||
**/
|
||||
static int file_archive_decompress_data_to_file(
|
||||
file_archive_transfer_t *transfer,
|
||||
file_archive_file_handle_t *handle,
|
||||
const char *path,
|
||||
uint32_t size,
|
||||
uint32_t checksum)
|
||||
{
|
||||
if (!handle)
|
||||
return 0;
|
||||
|
||||
#if 0
|
||||
handle->real_checksum = transfer->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))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state)
|
||||
{
|
||||
if (!state || !state->archive_file)
|
||||
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->transfer = state;
|
||||
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 (state->backend)
|
||||
{
|
||||
int ret = state->backend->archive_parse_file_iterate_step(
|
||||
state->context, valid_exts, userdata, file_cb);
|
||||
|
||||
if (ret == 1)
|
||||
state->step_current++; /* found another file */
|
||||
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->context)
|
||||
{
|
||||
if (state->backend->archive_parse_file_free)
|
||||
state->backend->archive_parse_file_free(state->context);
|
||||
state->context = NULL;
|
||||
}
|
||||
|
||||
if (state->archive_file)
|
||||
{
|
||||
filestream_close(state->archive_file);
|
||||
state->archive_file = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
if (state->archive_mmap_data)
|
||||
{
|
||||
munmap(state->archive_mmap_data, (size_t)state->archive_size);
|
||||
close(state->archive_mmap_fd);
|
||||
state->archive_mmap_fd = 0;
|
||||
state->archive_mmap_data = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (userdata)
|
||||
userdata->transfer = 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_file = NULL;
|
||||
#ifdef HAVE_MMAP
|
||||
state.archive_mmap_fd = 0;
|
||||
state.archive_mmap_data = NULL;
|
||||
#endif
|
||||
state.archive_size = 0;
|
||||
state.context = NULL;
|
||||
state.step_total = 0;
|
||||
state.step_current = 0;
|
||||
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)
|
||||
{
|
||||
if (!state || state->step_total == 0)
|
||||
return 0;
|
||||
|
||||
return (int)((state->step_current * 100) / (state->step_total));
|
||||
}
|
||||
|
||||
/**
|
||||
* file_archive_extract_file:
|
||||
* @archive_path : filename path to 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(
|
||||
const char *archive_path,
|
||||
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.current_file_path[0] = '\0';
|
||||
userdata.first_extracted_file_path = NULL;
|
||||
userdata.extraction_directory = extraction_directory;
|
||||
userdata.ext = list;
|
||||
userdata.list = NULL;
|
||||
userdata.found_file = false;
|
||||
userdata.list_only = false;
|
||||
userdata.crc = 0;
|
||||
userdata.transfer = NULL;
|
||||
userdata.dec = NULL;
|
||||
|
||||
if (!list)
|
||||
{
|
||||
ret = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!file_archive_walk(archive_path, valid_exts,
|
||||
file_archive_extract_cb, &userdata))
|
||||
{
|
||||
/* Parsing file archive failed. */
|
||||
ret = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!userdata.found_file)
|
||||
{
|
||||
/* Didn't find any file that matched valid extensions
|
||||
* for libretro implementation. */
|
||||
ret = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!string_is_empty(userdata.first_extracted_file_path))
|
||||
strlcpy(out_path, userdata.first_extracted_file_path, len);
|
||||
|
||||
end:
|
||||
if (userdata.first_extracted_file_path)
|
||||
free(userdata.first_extracted_file_path);
|
||||
if (list)
|
||||
string_list_free(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Warning: 'list' must zero initialised before
|
||||
* calling this function, otherwise memory leaks/
|
||||
* undefined behaviour will occur */
|
||||
bool file_archive_get_file_list_noalloc(struct string_list *list,
|
||||
const char *path,
|
||||
const char *valid_exts)
|
||||
{
|
||||
struct archive_extract_userdata userdata;
|
||||
|
||||
if (!list || !string_list_initialize(list))
|
||||
return false;
|
||||
|
||||
strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path));
|
||||
userdata.current_file_path[0] = '\0';
|
||||
userdata.first_extracted_file_path = NULL;
|
||||
userdata.extraction_directory = NULL;
|
||||
userdata.ext = NULL;
|
||||
userdata.list = list;
|
||||
userdata.found_file = false;
|
||||
userdata.list_only = true;
|
||||
userdata.crc = 0;
|
||||
userdata.transfer = NULL;
|
||||
userdata.dec = NULL;
|
||||
|
||||
if (!file_archive_walk(path, valid_exts,
|
||||
file_archive_get_file_list_cb, &userdata))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
struct archive_extract_userdata userdata;
|
||||
|
||||
strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path));
|
||||
userdata.current_file_path[0] = '\0';
|
||||
userdata.first_extracted_file_path = NULL;
|
||||
userdata.extraction_directory = NULL;
|
||||
userdata.ext = NULL;
|
||||
userdata.list = string_list_new();
|
||||
userdata.found_file = false;
|
||||
userdata.list_only = true;
|
||||
userdata.crc = 0;
|
||||
userdata.transfer = NULL;
|
||||
userdata.dec = NULL;
|
||||
|
||||
if (!userdata.list)
|
||||
return NULL;
|
||||
if (!file_archive_walk(path, valid_exts,
|
||||
file_archive_get_file_list_cb, &userdata))
|
||||
{
|
||||
string_list_free(userdata.list);
|
||||
return NULL;
|
||||
}
|
||||
return userdata.list;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
file_archive_file_handle_t handle;
|
||||
int ret;
|
||||
|
||||
if (!userdata->transfer || !userdata->transfer->backend)
|
||||
return false;
|
||||
|
||||
handle.data = NULL;
|
||||
handle.real_checksum = 0;
|
||||
|
||||
if (!userdata->transfer->backend->stream_decompress_data_to_file_init(
|
||||
userdata->transfer->context, &handle, cdata, cmode, csize, size))
|
||||
return false;
|
||||
|
||||
do
|
||||
{
|
||||
ret = userdata->transfer->backend->stream_decompress_data_to_file_iterate(
|
||||
userdata->transfer->context, &handle);
|
||||
}while (ret == 0);
|
||||
|
||||
if (ret == -1 || !file_archive_decompress_data_to_file(
|
||||
userdata->transfer, &handle, path,
|
||||
size, crc32))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
struct string_list *str_list = NULL;
|
||||
|
||||
/* Safety check.
|
||||
* If optional_filename and optional_filename
|
||||
* exists, we simply return 0,
|
||||
* hoping that optional_filename is the
|
||||
* same as requested.
|
||||
*/
|
||||
if (optional_filename && path_is_valid(optional_filename))
|
||||
{
|
||||
*length = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
str_list = file_archive_filename_split(path);
|
||||
/* 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)
|
||||
{
|
||||
/* could not extract string and substring. */
|
||||
string_list_free(str_list);
|
||||
*length = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
string_list_free(str_list);
|
||||
|
||||
if (*length != -1)
|
||||
return 1;
|
||||
|
||||
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)
|
||||
{
|
||||
#if defined(HAVE_7ZIP) || defined(HAVE_ZLIB)
|
||||
char newpath[PATH_MAX_LENGTH];
|
||||
const char *file_ext = NULL;
|
||||
char *last = NULL;
|
||||
|
||||
strlcpy(newpath, path, sizeof(newpath));
|
||||
|
||||
if ((last = (char*)path_get_archive_delim(newpath)))
|
||||
*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
|
||||
#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;
|
||||
struct archive_extract_userdata userdata = {0};
|
||||
bool returnerr = false;
|
||||
const char *archive_path = NULL;
|
||||
bool 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_file = NULL;
|
||||
#ifdef HAVE_MMAP
|
||||
state.archive_mmap_fd = 0;
|
||||
state.archive_mmap_data = NULL;
|
||||
#endif
|
||||
state.archive_size = 0;
|
||||
state.context = NULL;
|
||||
state.step_total = 0;
|
||||
state.step_current = 0;
|
||||
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.current_file_path, archive_path))
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
file_archive_parse_file_iterate_stop(&state);
|
||||
|
||||
return userdata.crc;
|
||||
}
|
||||
526
src/minarch/libretro-common/file/archive_file_7z.c
Normal file
526
src/minarch/libretro-common/file/archive_file_7z.c
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
#define SEVENZIP_LOOKTOREAD_BUF_SIZE (1 << 14)
|
||||
|
||||
/* 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
|
||||
{
|
||||
uint8_t *output;
|
||||
CFileInStream archiveStream;
|
||||
CLookToRead2 lookStream;
|
||||
ISzAlloc allocImp;
|
||||
ISzAlloc allocTempImp;
|
||||
CSzArEx db;
|
||||
size_t temp_size;
|
||||
uint32_t parse_index;
|
||||
uint32_t decompress_index;
|
||||
uint32_t packIndex;
|
||||
uint32_t block_index;
|
||||
};
|
||||
|
||||
static void *sevenzip_stream_alloc_impl(ISzAllocPtr p, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return 0;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void sevenzip_stream_free_impl(ISzAllocPtr p, void *address)
|
||||
{
|
||||
(void)p;
|
||||
|
||||
if (address)
|
||||
free(address);
|
||||
}
|
||||
|
||||
static void *sevenzip_stream_alloc_tmp_impl(ISzAllocPtr 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->lookStream.bufSize = SEVENZIP_LOOKTOREAD_BUF_SIZE * sizeof(Byte);
|
||||
sevenzip_context->lookStream.buf = (Byte*)malloc(sevenzip_context->lookStream.bufSize);
|
||||
|
||||
if (!sevenzip_context->lookStream.buf)
|
||||
sevenzip_context->lookStream.bufSize = 0;
|
||||
|
||||
return sevenzip_context;
|
||||
}
|
||||
|
||||
static void sevenzip_parse_file_free(void *context)
|
||||
{
|
||||
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)context;
|
||||
|
||||
if (!sevenzip_context)
|
||||
return;
|
||||
|
||||
if (sevenzip_context->output)
|
||||
{
|
||||
IAlloc_Free(&sevenzip_context->allocImp, sevenzip_context->output);
|
||||
sevenzip_context->output = NULL;
|
||||
}
|
||||
|
||||
SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp);
|
||||
File_Close(&sevenzip_context->archiveStream.file);
|
||||
|
||||
if (sevenzip_context->lookStream.buf)
|
||||
free(sevenzip_context->lookStream.buf);
|
||||
|
||||
free(sevenzip_context);
|
||||
}
|
||||
|
||||
/* 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 int64_t sevenzip_file_read(
|
||||
const char *path,
|
||||
const char *needle, void **buf,
|
||||
const char *optional_outfile)
|
||||
{
|
||||
CFileInStream archiveStream;
|
||||
CLookToRead2 lookStream;
|
||||
ISzAlloc allocImp;
|
||||
ISzAlloc allocTempImp;
|
||||
CSzArEx db;
|
||||
uint8_t *output = 0;
|
||||
int64_t 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;
|
||||
|
||||
lookStream.bufSize = SEVENZIP_LOOKTOREAD_BUF_SIZE * sizeof(Byte);
|
||||
lookStream.buf = (Byte*)malloc(lookStream.bufSize);
|
||||
|
||||
if (!lookStream.buf)
|
||||
lookStream.bufSize = 0;
|
||||
|
||||
#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);
|
||||
LookToRead2_CreateVTable(&lookStream, false);
|
||||
lookStream.realStream = &archiveStream.vt;
|
||||
LookToRead2_Init(&lookStream);
|
||||
CrcGenerateTable();
|
||||
|
||||
memset(&db, 0, sizeof(db));
|
||||
|
||||
SzArEx_Init(&db);
|
||||
|
||||
if (SzArEx_Open(&db, &lookStream.vt, &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.NumFiles; i++)
|
||||
{
|
||||
size_t len;
|
||||
char infile[PATH_MAX_LENGTH];
|
||||
size_t offset = 0;
|
||||
size_t outSizeProcessed = 0;
|
||||
|
||||
/* We skip over everything which is not a directory.
|
||||
* FIXME: Why continue then if IsDir is true?*/
|
||||
if (SzArEx_IsDir(&db, i))
|
||||
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.vt, i, &block_index,
|
||||
&output, &output_size, &offset, &outSizeProcessed,
|
||||
&allocImp, &allocTempImp);
|
||||
|
||||
if (res != SZ_OK)
|
||||
break; /* This goes to the error section. */
|
||||
|
||||
outsize = (int64_t)outSizeProcessed;
|
||||
|
||||
if (optional_outfile)
|
||||
{
|
||||
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((size_t)(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);
|
||||
|
||||
if (lookStream.buf)
|
||||
free(lookStream.buf);
|
||||
|
||||
return outsize;
|
||||
}
|
||||
|
||||
static bool sevenzip_stream_decompress_data_to_file_init(
|
||||
void *context, file_archive_file_handle_t *handle,
|
||||
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
|
||||
{
|
||||
struct sevenzip_context_t *sevenzip_context =
|
||||
(struct sevenzip_context_t*)context;
|
||||
|
||||
if (!sevenzip_context)
|
||||
return false;
|
||||
|
||||
sevenzip_context->decompress_index = (uint32_t)(size_t)cdata;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int sevenzip_stream_decompress_data_to_file_iterate(
|
||||
void *context, file_archive_file_handle_t *handle)
|
||||
{
|
||||
struct sevenzip_context_t *sevenzip_context =
|
||||
(struct sevenzip_context_t*)context;
|
||||
|
||||
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.vt, sevenzip_context->decompress_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 (handle)
|
||||
handle->data = sevenzip_context->output + offset;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sevenzip_parse_file_init(file_archive_transfer_t *state,
|
||||
const char *file)
|
||||
{
|
||||
uint8_t magic_buf[SEVENZIP_MAGIC_LEN];
|
||||
struct sevenzip_context_t *sevenzip_context = NULL;
|
||||
|
||||
if (state->archive_size < SEVENZIP_MAGIC_LEN)
|
||||
goto error;
|
||||
|
||||
filestream_seek(state->archive_file, 0, SEEK_SET);
|
||||
if (filestream_read(state->archive_file, magic_buf, SEVENZIP_MAGIC_LEN) != SEVENZIP_MAGIC_LEN)
|
||||
goto error;
|
||||
|
||||
if (string_is_not_equal_fast(magic_buf, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN))
|
||||
goto error;
|
||||
|
||||
sevenzip_context = (struct sevenzip_context_t*)sevenzip_stream_new();
|
||||
state->context = 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);
|
||||
LookToRead2_CreateVTable(&sevenzip_context->lookStream, false);
|
||||
sevenzip_context->lookStream.realStream = &sevenzip_context->archiveStream.vt;
|
||||
LookToRead2_Init(&sevenzip_context->lookStream);
|
||||
CrcGenerateTable();
|
||||
SzArEx_Init(&sevenzip_context->db);
|
||||
|
||||
if (SzArEx_Open(&sevenzip_context->db, &sevenzip_context->lookStream.vt,
|
||||
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp) != SZ_OK)
|
||||
goto error;
|
||||
|
||||
state->step_total = sevenzip_context->db.NumFiles;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (sevenzip_context)
|
||||
sevenzip_parse_file_free(sevenzip_context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sevenzip_parse_file_iterate_step_internal(
|
||||
struct sevenzip_context_t *sevenzip_context, char *filename,
|
||||
const uint8_t **cdata, unsigned *cmode,
|
||||
uint32_t *size, uint32_t *csize, uint32_t *checksum,
|
||||
unsigned *payback, struct archive_extract_userdata *userdata)
|
||||
{
|
||||
if (sevenzip_context->parse_index < sevenzip_context->db.NumFiles)
|
||||
{
|
||||
size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db,
|
||||
sevenzip_context->parse_index, NULL);
|
||||
uint64_t compressed_size = 0;
|
||||
|
||||
if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams)
|
||||
{
|
||||
compressed_size = sevenzip_context->db.db.PackPositions[sevenzip_context->packIndex + 1] -
|
||||
sevenzip_context->db.db.PackPositions[sevenzip_context->packIndex];
|
||||
|
||||
sevenzip_context->packIndex++;
|
||||
}
|
||||
|
||||
if (len < PATH_MAX_LENGTH &&
|
||||
!SzArEx_IsDir(&sevenzip_context->db, sevenzip_context->parse_index))
|
||||
{
|
||||
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->parse_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 = 0; /* unused for 7zip */
|
||||
*checksum = sevenzip_context->db.CRCs.Vals[sevenzip_context->parse_index];
|
||||
*size = (uint32_t)SzArEx_GetFileSize(&sevenzip_context->db, sevenzip_context->parse_index);
|
||||
*csize = (uint32_t)compressed_size;
|
||||
|
||||
*cdata = (uint8_t *)(size_t)sevenzip_context->parse_index;
|
||||
}
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
*payback = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sevenzip_parse_file_iterate_step(void *context,
|
||||
const char *valid_exts,
|
||||
struct archive_extract_userdata *userdata, file_archive_file_cb file_cb)
|
||||
{
|
||||
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 = (struct sevenzip_context_t*)context;
|
||||
int ret;
|
||||
|
||||
userdata->current_file_path[0] = '\0';
|
||||
|
||||
ret = sevenzip_parse_file_iterate_step_internal(sevenzip_context,
|
||||
userdata->current_file_path,
|
||||
&cdata, &cmode, &size, &csize,
|
||||
&checksum, &payload, userdata);
|
||||
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
|
||||
userdata->crc = checksum;
|
||||
|
||||
if (file_cb && !file_cb(userdata->current_file_path, valid_exts,
|
||||
cdata, cmode,
|
||||
csize, size, checksum, userdata))
|
||||
return 0;
|
||||
|
||||
sevenzip_context->parse_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_parse_file_init,
|
||||
sevenzip_parse_file_iterate_step,
|
||||
sevenzip_parse_file_free,
|
||||
sevenzip_stream_decompress_data_to_file_init,
|
||||
sevenzip_stream_decompress_data_to_file_iterate,
|
||||
sevenzip_stream_crc32_calculate,
|
||||
sevenzip_file_read,
|
||||
"7z"
|
||||
};
|
||||
571
src/minarch/libretro-common/file/archive_file_zlib.c
Normal file
571
src/minarch/libretro-common/file/archive_file_zlib.c
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
/* Copyright (C) 2010-2020 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 <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
|
||||
|
||||
enum file_archive_compression_mode
|
||||
{
|
||||
ZIP_MODE_STORED = 0,
|
||||
ZIP_MODE_DEFLATED = 8
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct file_archive_transfer *state;
|
||||
uint8_t *directory;
|
||||
uint8_t *directory_entry;
|
||||
uint8_t *directory_end;
|
||||
void *current_stream;
|
||||
uint8_t *compressed_data;
|
||||
uint8_t *decompressed_data;
|
||||
} zip_context_t;
|
||||
|
||||
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 zip_context_free_stream(
|
||||
zip_context_t *zip_context, bool keep_decompressed)
|
||||
{
|
||||
if (zip_context->current_stream)
|
||||
{
|
||||
zlib_inflate_backend.stream_free(zip_context->current_stream);
|
||||
zip_context->current_stream = NULL;
|
||||
}
|
||||
if (zip_context->compressed_data)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
if (!zip_context->state->archive_mmap_data)
|
||||
#endif
|
||||
{
|
||||
free(zip_context->compressed_data);
|
||||
zip_context->compressed_data = NULL;
|
||||
}
|
||||
}
|
||||
if (zip_context->decompressed_data && !keep_decompressed)
|
||||
{
|
||||
free(zip_context->decompressed_data);
|
||||
zip_context->decompressed_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool zlib_stream_decompress_data_to_file_init(
|
||||
void *context, file_archive_file_handle_t *handle,
|
||||
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
|
||||
{
|
||||
zip_context_t *zip_context = (zip_context_t *)context;
|
||||
struct file_archive_transfer *state = zip_context->state;
|
||||
uint8_t local_header_buf[4];
|
||||
uint8_t *local_header;
|
||||
uint32_t offsetNL, offsetEL;
|
||||
int64_t offsetData;
|
||||
|
||||
/* free previous data and stream if left unfinished */
|
||||
zip_context_free_stream(zip_context, false);
|
||||
|
||||
/* seek past most of the local directory header */
|
||||
#ifdef HAVE_MMAP
|
||||
if (state->archive_mmap_data)
|
||||
{
|
||||
local_header = state->archive_mmap_data + (size_t)cdata + 26;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
filestream_seek(state->archive_file, (int64_t)(size_t)cdata + 26, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(state->archive_file, local_header_buf, 4) != 4)
|
||||
goto error;
|
||||
local_header = local_header_buf;
|
||||
}
|
||||
|
||||
offsetNL = read_le(local_header, 2); /* file name length */
|
||||
offsetEL = read_le(local_header + 2, 2); /* extra field length */
|
||||
offsetData = (int64_t)(size_t)cdata + 26 + 4 + offsetNL + offsetEL;
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
if (state->archive_mmap_data)
|
||||
{
|
||||
zip_context->compressed_data = state->archive_mmap_data + (size_t)offsetData;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* allocate memory for the compressed data */
|
||||
zip_context->compressed_data = (uint8_t*)malloc(csize);
|
||||
if (!zip_context->compressed_data)
|
||||
goto error;
|
||||
|
||||
/* skip over name and extra data */
|
||||
filestream_seek(state->archive_file, offsetData, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(state->archive_file, zip_context->compressed_data, csize) != csize)
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (cmode)
|
||||
{
|
||||
case ZIP_MODE_STORED:
|
||||
handle->data = zip_context->compressed_data;
|
||||
return true;
|
||||
|
||||
case ZIP_MODE_DEFLATED:
|
||||
zip_context->current_stream = zlib_inflate_backend.stream_new();
|
||||
if (!zip_context->current_stream)
|
||||
goto error;
|
||||
|
||||
if (zlib_inflate_backend.define)
|
||||
zlib_inflate_backend.define(zip_context->current_stream, "window_bits", (uint32_t)-MAX_WBITS);
|
||||
|
||||
zip_context->decompressed_data = (uint8_t*)malloc(size);
|
||||
|
||||
if (!zip_context->decompressed_data)
|
||||
goto error;
|
||||
|
||||
zlib_inflate_backend.set_in(zip_context->current_stream,
|
||||
zip_context->compressed_data, csize);
|
||||
zlib_inflate_backend.set_out(zip_context->current_stream,
|
||||
zip_context->decompressed_data, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
error:
|
||||
zip_context_free_stream(zip_context, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int zlib_stream_decompress_data_to_file_iterate(
|
||||
void *context, file_archive_file_handle_t *handle)
|
||||
{
|
||||
zip_context_t *zip_context = (zip_context_t *)context;
|
||||
bool zstatus;
|
||||
uint32_t rd, wn;
|
||||
enum trans_stream_error terror;
|
||||
|
||||
if (!zip_context->current_stream)
|
||||
{
|
||||
/* file was uncompressed or decompression finished before */
|
||||
return 1;
|
||||
}
|
||||
|
||||
zstatus = zlib_inflate_backend.trans(zip_context->current_stream, false, &rd, &wn, &terror);
|
||||
|
||||
if (zstatus && !terror)
|
||||
{
|
||||
/* successfully decompressed entire file */
|
||||
zip_context_free_stream(zip_context, true);
|
||||
handle->data = zip_context->decompressed_data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
|
||||
{
|
||||
/* error during stream processing */
|
||||
zip_context_free_stream(zip_context, false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* still more data to process */
|
||||
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_transfer_t *transfer,
|
||||
file_archive_file_handle_t* handle,
|
||||
const uint8_t *cdata, unsigned cmode, uint32_t csize,
|
||||
uint32_t size, uint32_t crc32)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
transfer->backend = &zlib_backend;
|
||||
|
||||
if (!transfer->backend->stream_decompress_data_to_file_init(
|
||||
transfer->context, handle, cdata, cmode, csize, size))
|
||||
return false;
|
||||
|
||||
do
|
||||
{
|
||||
ret = transfer->backend->stream_decompress_data_to_file_iterate(
|
||||
transfer->context, handle);
|
||||
}while (ret == 0);
|
||||
|
||||
#if 0
|
||||
handle->real_checksum = transfer->backend->stream_crc_calculate(0,
|
||||
handle->data, size);
|
||||
|
||||
if (handle->real_checksum != crc32)
|
||||
{
|
||||
if (handle->data)
|
||||
free(handle->data);
|
||||
|
||||
handle->data = NULL;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *opt_file;
|
||||
char *needle;
|
||||
void **buf;
|
||||
size_t size;
|
||||
bool found;
|
||||
} decomp_state_t;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
decomp_state_t* decomp_state = (decomp_state_t*)userdata->cb_data;
|
||||
char last_char = name[strlen(name) - 1];
|
||||
/* Ignore directories. */
|
||||
if (last_char == '/' || last_char == '\\')
|
||||
return 1;
|
||||
|
||||
if (strstr(name, decomp_state->needle))
|
||||
{
|
||||
file_archive_file_handle_t handle = {0};
|
||||
|
||||
if (zip_file_decompressed_handle(userdata->transfer,
|
||||
&handle, cdata, cmode, csize, size, crc32))
|
||||
{
|
||||
if (decomp_state->opt_file != 0)
|
||||
{
|
||||
/* Called in case core has need_fullpath enabled. */
|
||||
bool success = filestream_write_file(decomp_state->opt_file, handle.data, size);
|
||||
|
||||
/* Note: Do not free handle.data here - this
|
||||
* will be done when stream is deinitialised */
|
||||
handle.data = NULL;
|
||||
|
||||
decomp_state->size = 0;
|
||||
|
||||
if (!success)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Called in case core has need_fullpath disabled.
|
||||
* Will move decompressed content directly into
|
||||
* RetroArch's ROM buffer. */
|
||||
zip_context_t *zip_context = (zip_context_t *)userdata->transfer->context;
|
||||
|
||||
decomp_state->size = 0;
|
||||
|
||||
/* Unlink data buffer from context (otherwise
|
||||
* it will be freed when the stream is deinitialised) */
|
||||
if (handle.data == zip_context->compressed_data)
|
||||
{
|
||||
/* Well this is fun...
|
||||
* If this is 'compressed' data (if the zip
|
||||
* file was created with the '-0 store only'
|
||||
* flag), and the origin file is mmapped, then
|
||||
* the context compressed_data buffer cannot be
|
||||
* reassigned (since it is not a traditional
|
||||
* block of user-assigned memory). We have to
|
||||
* create a copy of it instead... */
|
||||
#ifdef HAVE_MMAP
|
||||
if (zip_context->state->archive_mmap_data)
|
||||
{
|
||||
uint8_t *temp_buf = (uint8_t*)malloc(csize);
|
||||
|
||||
if (temp_buf)
|
||||
{
|
||||
memcpy(temp_buf, handle.data, csize);
|
||||
*decomp_state->buf = temp_buf;
|
||||
decomp_state->size = csize;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
*decomp_state->buf = handle.data;
|
||||
decomp_state->size = csize;
|
||||
zip_context->compressed_data = NULL;
|
||||
}
|
||||
}
|
||||
else if (handle.data == zip_context->decompressed_data)
|
||||
{
|
||||
*decomp_state->buf = handle.data;
|
||||
decomp_state->size = size;
|
||||
zip_context->decompressed_data = NULL;
|
||||
}
|
||||
|
||||
handle.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
decomp_state->found = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int64_t zip_file_read(
|
||||
const char *path,
|
||||
const char *needle, void **buf,
|
||||
const char *optional_outfile)
|
||||
{
|
||||
file_archive_transfer_t state = {0};
|
||||
decomp_state_t decomp = {0};
|
||||
struct archive_extract_userdata userdata = {0};
|
||||
bool returnerr = true;
|
||||
int ret = 0;
|
||||
|
||||
if (needle)
|
||||
decomp.needle = strdup(needle);
|
||||
if (optional_outfile)
|
||||
decomp.opt_file = strdup(optional_outfile);
|
||||
|
||||
state.type = ARCHIVE_TRANSFER_INIT;
|
||||
userdata.transfer = &state;
|
||||
userdata.cb_data = &decomp;
|
||||
decomp.buf = buf;
|
||||
|
||||
do
|
||||
{
|
||||
ret = file_archive_parse_file_iterate(&state, &returnerr, path,
|
||||
"", zip_file_decompressed, &userdata);
|
||||
if (!returnerr)
|
||||
break;
|
||||
}while (ret == 0 && !decomp.found);
|
||||
|
||||
file_archive_parse_file_iterate_stop(&state);
|
||||
|
||||
if (decomp.opt_file)
|
||||
free(decomp.opt_file);
|
||||
if (decomp.needle)
|
||||
free(decomp.needle);
|
||||
|
||||
if (!decomp.found)
|
||||
return -1;
|
||||
|
||||
return (int64_t)decomp.size;
|
||||
}
|
||||
|
||||
static int zip_parse_file_init(file_archive_transfer_t *state,
|
||||
const char *file)
|
||||
{
|
||||
uint8_t footer_buf[1024];
|
||||
uint8_t *footer = footer_buf;
|
||||
int64_t read_pos = state->archive_size;
|
||||
int64_t read_block = MIN(read_pos, sizeof(footer_buf));
|
||||
int64_t directory_size, directory_offset;
|
||||
zip_context_t *zip_context = NULL;
|
||||
|
||||
/* Minimal ZIP file size is 22 bytes */
|
||||
if (read_block < 22)
|
||||
return -1;
|
||||
|
||||
/* Find the end of central directory record by scanning
|
||||
* the file from the end towards the beginning.
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
if (--footer < footer_buf)
|
||||
{
|
||||
if (read_pos <= 0)
|
||||
return -1; /* reached beginning of file */
|
||||
|
||||
/* Read 21 bytes of overlaps except on the first block. */
|
||||
if (read_pos == state->archive_size)
|
||||
read_pos = read_pos - read_block;
|
||||
else
|
||||
read_pos = MAX(read_pos - read_block + 21, 0);
|
||||
|
||||
/* Seek to read_pos and read read_block bytes. */
|
||||
filestream_seek(state->archive_file, read_pos, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(state->archive_file, footer_buf, read_block) != read_block)
|
||||
return -1;
|
||||
|
||||
footer = footer_buf + read_block - 22;
|
||||
}
|
||||
if (read_le(footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
|
||||
{
|
||||
unsigned comment_len = read_le(footer + 20, 2);
|
||||
if (read_pos + (footer - footer_buf) + 22 + comment_len == state->archive_size)
|
||||
break; /* found it! */
|
||||
}
|
||||
}
|
||||
|
||||
/* Read directory info and do basic sanity checks. */
|
||||
directory_size = read_le(footer + 12, 4);
|
||||
directory_offset = read_le(footer + 16, 4);
|
||||
if (directory_size > state->archive_size
|
||||
|| directory_offset > state->archive_size)
|
||||
return -1;
|
||||
|
||||
/* This is a ZIP file, allocate one block of memory for both the
|
||||
* context and the entire directory, then read the directory.
|
||||
*/
|
||||
zip_context = (zip_context_t*)malloc(sizeof(zip_context_t) + (size_t)directory_size);
|
||||
zip_context->state = state;
|
||||
zip_context->directory = (uint8_t*)(zip_context + 1);
|
||||
zip_context->directory_entry = zip_context->directory;
|
||||
zip_context->directory_end = zip_context->directory + (size_t)directory_size;
|
||||
zip_context->current_stream = NULL;
|
||||
zip_context->compressed_data = NULL;
|
||||
zip_context->decompressed_data = NULL;
|
||||
|
||||
filestream_seek(state->archive_file, directory_offset, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(state->archive_file, zip_context->directory, directory_size) != directory_size)
|
||||
{
|
||||
free(zip_context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->context = zip_context;
|
||||
state->step_total = read_le(footer + 10, 2); /* total entries */;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zip_parse_file_iterate_step_internal(
|
||||
zip_context_t * zip_context, char *filename,
|
||||
const uint8_t **cdata,
|
||||
unsigned *cmode, uint32_t *size, uint32_t *csize,
|
||||
uint32_t *checksum, unsigned *payback)
|
||||
{
|
||||
uint8_t *entry = zip_context->directory_entry;
|
||||
uint32_t signature, namelength, extralength, commentlength, offset;
|
||||
|
||||
if (entry < zip_context->directory || entry >= zip_context->directory_end)
|
||||
return 0;
|
||||
|
||||
signature = read_le(zip_context->directory_entry + 0, 4);
|
||||
|
||||
if (signature != CENTRAL_FILE_HEADER_SIGNATURE)
|
||||
return 0;
|
||||
|
||||
*cmode = read_le(zip_context->directory_entry + 10, 2); /* compression mode, 0 = store, 8 = deflate */
|
||||
*checksum = read_le(zip_context->directory_entry + 16, 4); /* CRC32 */
|
||||
*csize = read_le(zip_context->directory_entry + 20, 4); /* compressed size */
|
||||
*size = read_le(zip_context->directory_entry + 24, 4); /* uncompressed size */
|
||||
|
||||
namelength = read_le(zip_context->directory_entry + 28, 2); /* file name length */
|
||||
extralength = read_le(zip_context->directory_entry + 30, 2); /* extra field length */
|
||||
commentlength = read_le(zip_context->directory_entry + 32, 2); /* file comment length */
|
||||
|
||||
if (namelength >= PATH_MAX_LENGTH)
|
||||
return -1;
|
||||
|
||||
memcpy(filename, zip_context->directory_entry + 46, namelength); /* file name */
|
||||
filename[namelength] = '\0';
|
||||
|
||||
offset = read_le(zip_context->directory_entry + 42, 4); /* relative offset of local file header */
|
||||
|
||||
*cdata = (uint8_t*)(size_t)offset; /* store file offset in data pointer */
|
||||
|
||||
*payback = 46 + namelength + extralength + commentlength;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int zip_parse_file_iterate_step(void *context,
|
||||
const char *valid_exts, struct archive_extract_userdata *userdata,
|
||||
file_archive_file_cb file_cb)
|
||||
{
|
||||
zip_context_t *zip_context = (zip_context_t *)context;
|
||||
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(zip_context,
|
||||
userdata->current_file_path, &cdata, &cmode, &size, &csize, &checksum, &payload);
|
||||
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
|
||||
userdata->crc = checksum;
|
||||
|
||||
if (file_cb && !file_cb(userdata->current_file_path, valid_exts, cdata, cmode,
|
||||
csize, size, checksum, userdata))
|
||||
return 0;
|
||||
|
||||
zip_context->directory_entry += payload;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void zip_parse_file_free(void *context)
|
||||
{
|
||||
zip_context_t *zip_context = (zip_context_t *)context;
|
||||
zip_context_free_stream(zip_context, false);
|
||||
free(zip_context);
|
||||
}
|
||||
|
||||
const struct file_archive_file_backend zlib_backend = {
|
||||
zip_parse_file_init,
|
||||
zip_parse_file_iterate_step,
|
||||
zip_parse_file_free,
|
||||
zlib_stream_decompress_data_to_file_init,
|
||||
zlib_stream_decompress_data_to_file_iterate,
|
||||
zlib_stream_crc32_calculate,
|
||||
zip_file_read,
|
||||
"zlib"
|
||||
};
|
||||
1515
src/minarch/libretro-common/file/config_file.c
Normal file
1515
src/minarch/libretro-common/file/config_file.c
Normal file
File diff suppressed because it is too large
Load diff
171
src/minarch/libretro-common/file/config_file_userdata.c
Normal file
171
src/minarch/libretro-common/file/config_file_userdata.c
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/* Copyright (C) 2010-2020 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_hex(void *userdata, const char *key_str,
|
||||
unsigned *value, unsigned 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_hex(usr->conf, key[0], value);
|
||||
got = got || config_get_hex(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 = {0};
|
||||
string_list_initialize(&list);
|
||||
string_split_noalloc(&list, 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_deinitialize(&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 = {0};
|
||||
string_list_initialize(&list);
|
||||
string_split_noalloc(&list, 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_deinitialize(&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);
|
||||
}
|
||||
1426
src/minarch/libretro-common/file/file_path.c
Normal file
1426
src/minarch/libretro-common/file/file_path.c
Normal file
File diff suppressed because it is too large
Load diff
150
src/minarch/libretro-common/file/file_path_io.c
Normal file
150
src/minarch/libretro-common/file/file_path_io.c
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (file_path_io.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <file/file_path.h>
|
||||
#include <retro_assert.h>
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <string/stdstring.h>
|
||||
#define VFS_FRONTEND
|
||||
#include <vfs/vfs_implementation.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h> /* stat() is defined here */
|
||||
#endif
|
||||
|
||||
/* TODO/FIXME - globals */
|
||||
static retro_vfs_stat_t path_stat_cb = retro_vfs_stat_impl;
|
||||
static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl;
|
||||
|
||||
void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
|
||||
{
|
||||
const struct retro_vfs_interface*
|
||||
vfs_iface = vfs_info->iface;
|
||||
|
||||
path_stat_cb = retro_vfs_stat_impl;
|
||||
path_mkdir_cb = retro_vfs_mkdir_impl;
|
||||
|
||||
if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface)
|
||||
return;
|
||||
|
||||
path_stat_cb = vfs_iface->stat;
|
||||
path_mkdir_cb = vfs_iface->mkdir;
|
||||
}
|
||||
|
||||
int path_stat(const char *path)
|
||||
{
|
||||
return path_stat_cb(path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* path_is_directory:
|
||||
* @path : path
|
||||
*
|
||||
* Checks if path is a directory.
|
||||
*
|
||||
* @return true if path is a directory, otherwise false.
|
||||
*/
|
||||
bool path_is_directory(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
bool path_is_character_special(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
|
||||
}
|
||||
|
||||
bool path_is_valid(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
|
||||
}
|
||||
|
||||
int32_t path_get_size(const char *path)
|
||||
{
|
||||
int32_t filesize = 0;
|
||||
if (path_stat_cb(path, &filesize) != 0)
|
||||
return filesize;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_mkdir:
|
||||
* @dir : directory
|
||||
*
|
||||
* Create directory on filesystem.
|
||||
*
|
||||
* Recursive function.
|
||||
*
|
||||
* @return true if directory could be created, otherwise false.
|
||||
**/
|
||||
bool path_mkdir(const char *dir)
|
||||
{
|
||||
bool norecurse = false;
|
||||
char *basedir = NULL;
|
||||
|
||||
if (!(dir && *dir))
|
||||
return false;
|
||||
|
||||
/* Use heap. Real chance of stack
|
||||
* overflow if we recurse too hard. */
|
||||
if (!(basedir = strdup(dir)))
|
||||
return false;
|
||||
|
||||
path_parent_dir(basedir, strlen(basedir));
|
||||
|
||||
if (!*basedir || !strcmp(basedir, dir))
|
||||
{
|
||||
free(basedir);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( path_is_directory(basedir)
|
||||
|| path_mkdir(basedir))
|
||||
norecurse = true;
|
||||
|
||||
free(basedir);
|
||||
|
||||
if (norecurse)
|
||||
{
|
||||
int ret = path_mkdir_cb(dir);
|
||||
|
||||
/* Don't treat this as an error. */
|
||||
if (ret == -2 && path_is_directory(dir))
|
||||
return true;
|
||||
else if (ret == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
103
src/minarch/libretro-common/file/nbio/nbio_intf.c
Normal file
103
src/minarch/libretro-common/file/nbio/nbio_intf.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/* Copyright (C) 2010-2020 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;
|
||||
|
||||
#ifndef _XBOX
|
||||
#if defined(_WIN32)
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1500
|
||||
|
||||
#ifndef HAVE_MMAP_WIN32
|
||||
#define HAVE_MMAP_WIN32
|
||||
#endif
|
||||
|
||||
#elif !defined(_MSC_VER)
|
||||
|
||||
#ifndef HAVE_MMAP_WIN32
|
||||
#define HAVE_MMAP_WIN32
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#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(HAVE_MMAP_WIN32)
|
||||
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);
|
||||
}
|
||||
235
src/minarch/libretro-common/file/nbio/nbio_linux.c
Normal file
235
src/minarch/libretro-common/file/nbio/nbio_linux.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/* Copyright (C) 2010-2020 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 <time.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/aio_abi.h>
|
||||
|
||||
struct nbio_linux_t
|
||||
{
|
||||
void* ptr;
|
||||
aio_context_t ctx;
|
||||
struct iocb cb;
|
||||
size_t len;
|
||||
int fd;
|
||||
bool busy;
|
||||
};
|
||||
|
||||
/* 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)
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
if (len < handle->len)
|
||||
abort();
|
||||
|
||||
if (ftruncate(handle->fd, len) != 0)
|
||||
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
|
||||
231
src/minarch/libretro-common/file/nbio/nbio_orbis.c
Normal file
231
src/minarch/libretro-common/file/nbio/nbio_orbis.c
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nbio_orbis.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(ORBIS)
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <orbisFile.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/fcntl.h>
|
||||
|
||||
struct nbio_orbis_t
|
||||
{
|
||||
void* data;
|
||||
size_t progress;
|
||||
size_t len;
|
||||
int fd;
|
||||
unsigned int mode;
|
||||
/*
|
||||
* possible values:
|
||||
* NBIO_READ, NBIO_WRITE - obvious
|
||||
* -1 - currently doing nothing
|
||||
* -2 - the pointer was reallocated since the last operation
|
||||
*/
|
||||
signed char op;
|
||||
};
|
||||
|
||||
static void *nbio_orbis_open(const char * filename, unsigned int mode)
|
||||
{
|
||||
static const int o_flags[] = { O_RDONLY, O_RDWR | O_CREAT | O_TRUNC,
|
||||
O_RDWR, O_RDONLY, O_RDWR | O_CREAT | O_TRUNC };
|
||||
void *buf = NULL;
|
||||
struct nbio_orbis_t* handle = NULL;
|
||||
size_t len = 0;
|
||||
int fd = orbisOpen(filename, o_flags[mode], 0644);
|
||||
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
handle = (struct nbio_orbis_t*)malloc(sizeof(struct nbio_orbis_t));
|
||||
|
||||
if (!handle)
|
||||
goto error;
|
||||
|
||||
handle->fd = fd;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case NBIO_WRITE:
|
||||
case BIO_WRITE:
|
||||
break;
|
||||
default:
|
||||
len=orbisLseek(handle->fd, 0, SEEK_END);
|
||||
orbisLseek(handle->fd, 0, SEEK_SET);
|
||||
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);
|
||||
orbisClose(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void nbio_orbis_begin_read(void *data)
|
||||
{
|
||||
|
||||
struct nbio_orbis_t *handle = (struct nbio_orbis_t*)data;
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->op >= 0)
|
||||
return;
|
||||
|
||||
orbisLseek(handle->fd, 0, SEEK_SET);
|
||||
|
||||
handle->op = NBIO_READ;
|
||||
handle->progress = 0;
|
||||
}
|
||||
|
||||
static void nbio_orbis_begin_write(void *data)
|
||||
{
|
||||
struct nbio_orbis_t *handle = (struct nbio_orbis_t*)data;
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->op >= 0)
|
||||
return;
|
||||
|
||||
orbisLseek(handle->fd, 0, SEEK_SET);
|
||||
handle->op = NBIO_WRITE;
|
||||
handle->progress = 0;
|
||||
}
|
||||
|
||||
static bool nbio_orbis_iterate(void *data)
|
||||
{
|
||||
size_t amount = 65536;
|
||||
struct nbio_orbis_t *handle = (struct nbio_orbis_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;
|
||||
break;
|
||||
case NBIO_WRITE:
|
||||
if (handle->mode == BIO_WRITE)
|
||||
{
|
||||
size_t written = 0;
|
||||
amount = handle->len;
|
||||
written = orbisWrite(handle->fd, (char*)handle->data, amount);
|
||||
|
||||
if (written != amount)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
handle->progress += amount;
|
||||
|
||||
if (handle->progress == handle->len)
|
||||
handle->op = -1;
|
||||
return (handle->op < 0);
|
||||
}
|
||||
|
||||
static void nbio_orbis_resize(void *data, size_t len)
|
||||
{
|
||||
struct nbio_orbis_t *handle = (struct nbio_orbis_t*)data;
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->op >= 0)
|
||||
return;
|
||||
if (len < handle->len)
|
||||
return;
|
||||
|
||||
handle->len = len;
|
||||
handle->data = realloc(handle->data, handle->len);
|
||||
handle->op = -1;
|
||||
handle->progress = handle->len;
|
||||
}
|
||||
|
||||
static void *nbio_orbis_get_ptr(void *data, size_t* len)
|
||||
{
|
||||
struct nbio_orbis_t *handle = (struct nbio_orbis_t*)data;
|
||||
if (!handle)
|
||||
return NULL;
|
||||
if (len)
|
||||
*len = handle->len;
|
||||
if (handle->op == -1)
|
||||
return handle->data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void nbio_orbis_cancel(void *data)
|
||||
{
|
||||
struct nbio_orbis_t *handle = (struct nbio_orbis_t*)data;
|
||||
if (!handle)
|
||||
return;
|
||||
handle->op = -1;
|
||||
handle->progress = handle->len;
|
||||
}
|
||||
|
||||
static void nbio_orbis_free(void *data)
|
||||
{
|
||||
struct nbio_orbis_t *handle = (struct nbio_orbis_t*)data;
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->op >= 0)
|
||||
return;
|
||||
|
||||
orbisClose(handle->fd);
|
||||
free(handle->data);
|
||||
|
||||
handle->data = NULL;
|
||||
free(handle);
|
||||
}
|
||||
|
||||
nbio_intf_t nbio_orbis = {
|
||||
nbio_orbis_open,
|
||||
nbio_orbis_begin_read,
|
||||
nbio_orbis_begin_write,
|
||||
nbio_orbis_iterate,
|
||||
nbio_orbis_resize,
|
||||
nbio_orbis_get_ptr,
|
||||
nbio_orbis_cancel,
|
||||
nbio_orbis_free,
|
||||
"nbio_orbis",
|
||||
};
|
||||
#endif
|
||||
303
src/minarch/libretro-common/file/nbio/nbio_stdio.c
Normal file
303
src/minarch/libretro-common/file/nbio/nbio_stdio.c
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
/* Copyright (C) 2010-2020 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>
|
||||
#if defined(WIIU)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||
#define ATLEAST_VC2005
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE - 0) >= 200112) || (defined(__POSIX_VISIBLE) && __POSIX_VISIBLE >= 200112) || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || __USE_LARGEFILE || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
|
||||
#ifndef HAVE_64BIT_OFFSETS
|
||||
#define HAVE_64BIT_OFFSETS
|
||||
#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 int64_t fseek_wrap(FILE *f, int64_t offset, int origin)
|
||||
{
|
||||
#ifdef ATLEAST_VC2005
|
||||
/* VC2005 and up have a special 64-bit fseek */
|
||||
return _fseeki64(f, offset, origin);
|
||||
#elif defined(HAVE_64BIT_OFFSETS)
|
||||
return fseeko(f, (off_t)offset, origin);
|
||||
#else
|
||||
return fseek(f, (long)offset, origin);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int64_t ftell_wrap(FILE *f)
|
||||
{
|
||||
#ifdef ATLEAST_VC2005
|
||||
/* VC2005 and up have a special 64-bit ftell */
|
||||
return _ftelli64(f);
|
||||
#elif defined(HAVE_64BIT_OFFSETS)
|
||||
return ftello(f);
|
||||
#else
|
||||
return ftell(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *nbio_stdio_open(const char * filename, unsigned mode)
|
||||
{
|
||||
void *buf = NULL;
|
||||
struct nbio_stdio_t* handle = NULL;
|
||||
int64_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_wrap(handle->f, 0, SEEK_END);
|
||||
len = ftell_wrap(handle->f);
|
||||
break;
|
||||
}
|
||||
|
||||
handle->mode = mode;
|
||||
|
||||
#if defined(WIIU)
|
||||
/* hit the aligned-buffer fast path on Wii U */
|
||||
if (len)
|
||||
buf = memalign(0x40, (size_t)len);
|
||||
#else
|
||||
if (len)
|
||||
buf = malloc((size_t)len);
|
||||
#endif
|
||||
|
||||
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)
|
||||
abort();
|
||||
|
||||
fseek_wrap(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)
|
||||
abort();
|
||||
|
||||
fseek_wrap(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)
|
||||
{
|
||||
void *new_data = NULL;
|
||||
struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->op >= 0)
|
||||
abort();
|
||||
if (len < handle->len)
|
||||
abort();
|
||||
|
||||
handle->len = len;
|
||||
handle->progress = len;
|
||||
handle->op = -1;
|
||||
|
||||
new_data = realloc(handle->data, handle->len);
|
||||
|
||||
if (new_data)
|
||||
handle->data = new_data;
|
||||
}
|
||||
|
||||
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)
|
||||
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",
|
||||
};
|
||||
184
src/minarch/libretro-common/file/nbio/nbio_unixmmap.c
Normal file
184
src/minarch/libretro-common/file/nbio/nbio_unixmmap.c
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
{
|
||||
void* ptr;
|
||||
size_t len;
|
||||
int fd;
|
||||
int map_flags;
|
||||
};
|
||||
|
||||
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 */
|
||||
abort();
|
||||
}
|
||||
|
||||
if (ftruncate(handle->fd, len) != 0)
|
||||
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)
|
||||
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
|
||||
237
src/minarch/libretro-common/file/nbio/nbio_windowsmmap.c
Normal file
237
src/minarch/libretro-common/file/nbio/nbio_windowsmmap.c
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/* Copyright (C) 2010-2020 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)
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1500
|
||||
|
||||
#ifndef HAVE_MMAP_WIN32
|
||||
#define HAVE_MMAP_WIN32
|
||||
#endif
|
||||
|
||||
#elif !defined(_MSC_VER)
|
||||
|
||||
#ifndef HAVE_MMAP_WIN32
|
||||
#define HAVE_MMAP_WIN32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MMAP_WIN32)
|
||||
|
||||
#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;
|
||||
void* ptr;
|
||||
size_t len;
|
||||
bool is_write;
|
||||
};
|
||||
|
||||
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);
|
||||
#ifdef __WINRT__
|
||||
HANDLE file = CreateFile2(filename_wide, access, FILE_SHARE_ALL, dispositions[mode], NULL);
|
||||
#else
|
||||
HANDLE file = CreateFileW(filename_wide, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
#endif
|
||||
|
||||
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. */
|
||||
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))
|
||||
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)
|
||||
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
|
||||
122
src/minarch/libretro-common/file/retro_dirent.c
Normal file
122
src/minarch/libretro-common/file/retro_dirent.c
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* Copyright (C) 2010-2020 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>
|
||||
#define VFS_FRONTEND
|
||||
#include <vfs/vfs_implementation.h>
|
||||
|
||||
/* TODO/FIXME - static globals */
|
||||
static retro_vfs_opendir_t dirent_opendir_cb = NULL;
|
||||
static retro_vfs_readdir_t dirent_readdir_cb = NULL;
|
||||
static retro_vfs_dirent_get_name_t dirent_dirent_get_name_cb = NULL;
|
||||
static retro_vfs_dirent_is_dir_t dirent_dirent_is_dir_cb = NULL;
|
||||
static retro_vfs_closedir_t dirent_closedir_cb = NULL;
|
||||
|
||||
void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info)
|
||||
{
|
||||
const struct retro_vfs_interface* vfs_iface;
|
||||
|
||||
dirent_opendir_cb = NULL;
|
||||
dirent_readdir_cb = NULL;
|
||||
dirent_dirent_get_name_cb = NULL;
|
||||
dirent_dirent_is_dir_cb = NULL;
|
||||
dirent_closedir_cb = NULL;
|
||||
|
||||
vfs_iface = vfs_info->iface;
|
||||
|
||||
if (
|
||||
vfs_info->required_interface_version < DIRENT_REQUIRED_VFS_VERSION ||
|
||||
!vfs_iface)
|
||||
return;
|
||||
|
||||
dirent_opendir_cb = vfs_iface->opendir;
|
||||
dirent_readdir_cb = vfs_iface->readdir;
|
||||
dirent_dirent_get_name_cb = vfs_iface->dirent_get_name;
|
||||
dirent_dirent_is_dir_cb = vfs_iface->dirent_is_dir;
|
||||
dirent_closedir_cb = vfs_iface->closedir;
|
||||
}
|
||||
|
||||
struct RDIR *retro_opendir_include_hidden(
|
||||
const char *name, bool include_hidden)
|
||||
{
|
||||
if (dirent_opendir_cb)
|
||||
return (struct RDIR *)dirent_opendir_cb(name, include_hidden);
|
||||
return (struct RDIR *)retro_vfs_opendir_impl(name, include_hidden);
|
||||
}
|
||||
|
||||
struct RDIR *retro_opendir(const char *name)
|
||||
{
|
||||
return retro_opendir_include_hidden(name, false);
|
||||
}
|
||||
|
||||
bool retro_dirent_error(struct RDIR *rdir)
|
||||
{
|
||||
/* Left for compatibility */
|
||||
return false;
|
||||
}
|
||||
|
||||
int retro_readdir(struct RDIR *rdir)
|
||||
{
|
||||
if (dirent_readdir_cb)
|
||||
return dirent_readdir_cb((struct retro_vfs_dir_handle *)rdir);
|
||||
return retro_vfs_readdir_impl((struct retro_vfs_dir_handle *)rdir);
|
||||
}
|
||||
|
||||
const char *retro_dirent_get_name(struct RDIR *rdir)
|
||||
{
|
||||
if (dirent_dirent_get_name_cb)
|
||||
return dirent_dirent_get_name_cb((struct retro_vfs_dir_handle *)rdir);
|
||||
return retro_vfs_dirent_get_name_impl((struct retro_vfs_dir_handle *)rdir);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* retro_dirent_is_dir:
|
||||
* @rdir : pointer to the directory entry.
|
||||
* @unused : deprecated, included for compatibility reasons, pass NULL
|
||||
*
|
||||
* 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 *unused)
|
||||
{
|
||||
if (dirent_dirent_is_dir_cb)
|
||||
return dirent_dirent_is_dir_cb((struct retro_vfs_dir_handle *)rdir);
|
||||
return retro_vfs_dirent_is_dir_impl((struct retro_vfs_dir_handle *)rdir);
|
||||
}
|
||||
|
||||
void retro_closedir(struct RDIR *rdir)
|
||||
{
|
||||
if (dirent_closedir_cb)
|
||||
dirent_closedir_cb((struct retro_vfs_dir_handle *)rdir);
|
||||
else
|
||||
retro_vfs_closedir_impl((struct retro_vfs_dir_handle *)rdir);
|
||||
}
|
||||
785
src/minarch/libretro-common/formats/bmp/rbmp.c
Normal file
785
src/minarch/libretro-common/formats/bmp/rbmp.c
Normal file
|
|
@ -0,0 +1,785 @@
|
|||
/* Copyright (C) 2010-2020 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
|
||||
{
|
||||
unsigned char *img_buffer;
|
||||
unsigned char *img_buffer_end;
|
||||
unsigned char *img_buffer_original;
|
||||
int img_n;
|
||||
int img_out_n;
|
||||
int buflen;
|
||||
uint32_t img_x;
|
||||
uint32_t img_y;
|
||||
unsigned char buffer_start[128];
|
||||
} 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)
|
||||
{
|
||||
return rbmp_get8(s) + (rbmp_get8(s) << 8);
|
||||
}
|
||||
|
||||
#define RBMP_GET32LE(s) (rbmp_get16le(s) + (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 10:
|
||||
for (i = x-1; i >= 0; --i, src += 1, dest += 2)
|
||||
{
|
||||
dest[0]=src[0];
|
||||
dest[1]=255;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
for (i = x-1; i >= 0; --i, src += 1, dest += 3)
|
||||
dest[0]=dest[1]=dest[2]=src[0];
|
||||
break;
|
||||
case 12:
|
||||
for (i = x-1; i >= 0; --i, src += 1, dest += 4)
|
||||
{
|
||||
dest[0]=dest[1]=dest[2]=src[0];
|
||||
dest[3]=255;
|
||||
}
|
||||
break;
|
||||
case 17:
|
||||
for (i = x-1; i >= 0; --i, src += 2, dest += 1)
|
||||
dest[0]=src[0];
|
||||
break;
|
||||
case 19:
|
||||
for (i = x-1; i >= 0; --i, src += 2, dest += 3)
|
||||
dest[0]=dest[1]=dest[2]=src[0];
|
||||
break;
|
||||
case 20:
|
||||
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 28:
|
||||
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 25:
|
||||
for (i = x-1; i >= 0; --i, src += 3, dest += 1)
|
||||
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
|
||||
break;
|
||||
case 26:
|
||||
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 33:
|
||||
for (i = x-1; i >= 0; --i, src += 4, dest += 1)
|
||||
dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
|
||||
break;
|
||||
case 34:
|
||||
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 35:
|
||||
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;
|
||||
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 = bits;
|
||||
|
||||
if (shift < 0)
|
||||
v <<= -shift;
|
||||
else
|
||||
v >>= shift;
|
||||
|
||||
result = v;
|
||||
|
||||
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;
|
||||
|
||||
/* discard filesize */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
/* discard reserved */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
|
||||
offset = (uint32_t)RBMP_GET32LE(s);
|
||||
hsz = (uint32_t)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 = (uint32_t)RBMP_GET32LE(s);
|
||||
s->img_y = (uint32_t)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 = (uint32_t)RBMP_GET32LE(s);
|
||||
|
||||
/* BMP RLE type not supported? */
|
||||
if (compress == 1 || compress == 2)
|
||||
return 0;
|
||||
|
||||
/* discard sizeof */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
/* discard hres */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
/* discard vres */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
/* discard colors used */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
/* discard max important */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
|
||||
if (hsz == 40 || hsz == 56)
|
||||
{
|
||||
if (hsz == 56)
|
||||
{
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(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 = (uint32_t)RBMP_GET32LE(s);
|
||||
mg = (uint32_t)RBMP_GET32LE(s);
|
||||
mb = (uint32_t)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 = (uint32_t)RBMP_GET32LE(s);
|
||||
mg = (uint32_t)RBMP_GET32LE(s);
|
||||
mb = (uint32_t)RBMP_GET32LE(s);
|
||||
ma = (uint32_t)RBMP_GET32LE(s);
|
||||
/* Discard color space */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
for (i = 0; i < 12; ++i)
|
||||
{
|
||||
/* Discard color space parameters */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
}
|
||||
if (hsz == 124)
|
||||
{
|
||||
/* Discard rendering intent */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
/* Discard offset of profile data */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
/* Discard size of profile data */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
/* Discard reserved */
|
||||
rbmp_get16le(s);
|
||||
rbmp_get16le(s);
|
||||
}
|
||||
}
|
||||
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);
|
||||
int 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)
|
||||
{
|
||||
if (target == 4)
|
||||
{
|
||||
/* Need to apply alpha channel as well */
|
||||
if (easy == 2)
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
out[z+2] = rbmp_get8(s);
|
||||
out[z+1] = rbmp_get8(s);
|
||||
out[z+0] = rbmp_get8(s);
|
||||
z += 3;
|
||||
out[z++] = rbmp_get8(s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
out[z+2] = rbmp_get8(s);
|
||||
out[z+1] = rbmp_get8(s);
|
||||
out[z+0] = rbmp_get8(s);
|
||||
z += 3;
|
||||
out[z++] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
out[z+2] = rbmp_get8(s);
|
||||
out[z+1] = rbmp_get8(s);
|
||||
out[z+0] = rbmp_get8(s);
|
||||
z += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target == 4)
|
||||
{
|
||||
/* Need to apply alpha channel as well */
|
||||
if (ma)
|
||||
{
|
||||
if (bpp == 16)
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
uint32_t v = (uint32_t)rbmp_get16le(s);
|
||||
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));
|
||||
out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & ma, ashift, acount));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
uint32_t v = (uint32_t)RBMP_GET32LE(s);
|
||||
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));
|
||||
out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & ma, ashift, acount));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bpp == 16)
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
uint32_t v = (uint32_t)rbmp_get16le(s);
|
||||
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));
|
||||
out[z++] = RBMP_BYTECAST(255);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
uint32_t v = (uint32_t)RBMP_GET32LE(s);
|
||||
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));
|
||||
out[z++] = RBMP_BYTECAST(255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bpp == 16)
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
uint32_t v = (uint32_t)rbmp_get16le(s);
|
||||
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));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < (int) s->img_x; ++i)
|
||||
{
|
||||
uint32_t v = (uint32_t)RBMP_GET32LE(s);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
237
src/minarch/libretro-common/formats/bmp/rbmp_encode.c
Normal file
237
src/minarch/libretro-common/formats/bmp/rbmp_encode.c
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/* Copyright (C) 2010-2020 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>
|
||||
|
||||
void form_bmp_header(uint8_t *header,
|
||||
unsigned width, unsigned height,
|
||||
bool is32bpp)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static bool write_header_bmp(RFILE *file, unsigned width, unsigned height, bool is32bpp)
|
||||
{
|
||||
uint8_t header[54];
|
||||
form_bmp_header(header, width, height, is32bpp);
|
||||
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;
|
||||
}
|
||||
651
src/minarch/libretro-common/formats/cdfs/cdfs.c
Normal file
651
src/minarch/libretro-common/formats/cdfs/cdfs.c
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
#include <formats/cdfs.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <compat/strl.h>
|
||||
#include <file/file_path.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
#include <streams/chd_stream.h>
|
||||
#endif
|
||||
|
||||
static void cdfs_determine_sector_size(cdfs_track_t* track)
|
||||
{
|
||||
uint8_t buffer[32];
|
||||
const int toc_sector = 16;
|
||||
|
||||
/* MODE information is normally found in the CUE sheet, but we can try to determine it from the raw data.
|
||||
*
|
||||
* MODE1/2048 - CDROM Mode1 Data (cooked) [no header, no footer]
|
||||
* MODE1/2352 - CDROM Mode1 Data (raw) [16 byte header, 288 byte footer]
|
||||
* MODE2/2336 - CDROM-XA Mode2 Data [8 byte header, 280 byte footer]
|
||||
* MODE2/2352 - CDROM-XA Mode2 Data [24 byte header, 280 byte footer]
|
||||
*
|
||||
* Note that MODE is actually a property on each sector and can change between 1 and 2 depending on how much error
|
||||
* correction the author desired. To support that, the data format must be "/2352" to include the full header and
|
||||
* data without error correction information, at which point the CUE sheet information becomes just a hint.
|
||||
*/
|
||||
|
||||
/* The boot record or primary volume descriptor is always at sector 16 and will contain a "CD001" marker */
|
||||
intfstream_seek(track->stream, toc_sector * 2352 + track->first_sector_offset, SEEK_SET);
|
||||
if (intfstream_read(track->stream, buffer, sizeof(buffer)) < sizeof(buffer))
|
||||
return;
|
||||
|
||||
/* if this is a CDROM-XA data source, the "CD001" tag will be 25 bytes into the sector */
|
||||
if (buffer[25] == 0x43 && buffer[26] == 0x44 &&
|
||||
buffer[27] == 0x30 && buffer[28] == 0x30 && buffer[29] == 0x31)
|
||||
{
|
||||
track->stream_sector_size = 2352;
|
||||
track->stream_sector_header_size = 24;
|
||||
}
|
||||
/* otherwise it should be 17 bytes into the sector */
|
||||
else if (buffer[17] == 0x43 && buffer[18] == 0x44 &&
|
||||
buffer[19] == 0x30 && buffer[20] == 0x30 && buffer[21] == 0x31)
|
||||
{
|
||||
track->stream_sector_size = 2352;
|
||||
track->stream_sector_header_size = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ISO-9660 says the first twelve bytes of a sector should be the sync pattern 00 FF FF FF FF FF FF FF FF FF FF 00 */
|
||||
if (buffer[0] == 0 && buffer[1] == 0xFF && buffer[2] == 0xFF && buffer[3] == 0xFF &&
|
||||
buffer[4] == 0xFF && buffer[5] == 0xFF && buffer[6] == 0xFF && buffer[7] == 0xFF &&
|
||||
buffer[8] == 0xFF && buffer[9] == 0xFF && buffer[10] == 0xFF && buffer[11] == 0)
|
||||
{
|
||||
/* if we didn't find a CD001 tag, this format may predate ISO-9660 */
|
||||
|
||||
/* after the 12 byte sync pattern is three bytes identifying the sector and then one byte for the mode (total 16 bytes) */
|
||||
track->stream_sector_size = 2352;
|
||||
track->stream_sector_header_size = 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cdfs_determine_sector_size_from_file_size(cdfs_track_t* track)
|
||||
{
|
||||
/* attempt to determine stream_sector_size from file size */
|
||||
size_t size = intfstream_get_size(track->stream);
|
||||
|
||||
if ((size % 2352) == 0)
|
||||
{
|
||||
/* raw tracks use all 2352 bytes and have a 24 byte header */
|
||||
track->stream_sector_size = 2352;
|
||||
track->stream_sector_header_size = 24;
|
||||
}
|
||||
else if ((size % 2048) == 0)
|
||||
{
|
||||
/* cooked tracks eliminate all header/footer data */
|
||||
track->stream_sector_size = 2048;
|
||||
track->stream_sector_header_size = 0;
|
||||
}
|
||||
else if ((size % 2336) == 0)
|
||||
{
|
||||
/* MODE 2 format without 16-byte sync data */
|
||||
track->stream_sector_size = 2336;
|
||||
track->stream_sector_header_size = 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void cdfs_seek_track_sector(cdfs_track_t* track, unsigned int sector)
|
||||
{
|
||||
intfstream_seek(track->stream,
|
||||
sector * track->stream_sector_size +
|
||||
track->stream_sector_header_size +
|
||||
track->first_sector_offset, SEEK_SET);
|
||||
}
|
||||
|
||||
void cdfs_seek_sector(cdfs_file_t* file, unsigned int sector)
|
||||
{
|
||||
/* only allowed if open_file was called with a NULL path */
|
||||
if (file->first_sector == 0)
|
||||
{
|
||||
if (sector != file->current_sector)
|
||||
{
|
||||
file->current_sector = sector;
|
||||
file->sector_buffer_valid = 0;
|
||||
}
|
||||
|
||||
file->pos = file->current_sector * 2048;
|
||||
file->current_sector_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cdfs_get_num_sectors(cdfs_file_t* file)
|
||||
{
|
||||
uint32_t frame_size = intfstream_get_frame_size(file->track->stream);
|
||||
if (frame_size == 0)
|
||||
{
|
||||
frame_size = file->track->stream_sector_size;
|
||||
if (frame_size == 0)
|
||||
frame_size = 1; /* prevent divide by 0 error if sector size is unknown */
|
||||
}
|
||||
return intfstream_get_size(file->track->stream) / frame_size;
|
||||
}
|
||||
|
||||
uint32_t cdfs_get_first_sector(cdfs_file_t* file)
|
||||
{
|
||||
return file->track->first_sector_index;
|
||||
}
|
||||
|
||||
static int cdfs_find_file(cdfs_file_t* file, const char* path)
|
||||
{
|
||||
size_t path_length;
|
||||
int sector;
|
||||
uint8_t buffer[2048], *tmp;
|
||||
const char* slash = strrchr(path, '\\');
|
||||
|
||||
if (slash)
|
||||
{
|
||||
/* navigate the path to the directory record for the file */
|
||||
const int dir_length = (int)(slash - path);
|
||||
memcpy(buffer, path, dir_length);
|
||||
buffer[dir_length] = '\0';
|
||||
|
||||
sector = cdfs_find_file(file, (const char*)buffer);
|
||||
if (sector < 0)
|
||||
return sector;
|
||||
|
||||
path += dir_length + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset;
|
||||
|
||||
/* find the CD information (always 16 frames in) */
|
||||
cdfs_seek_track_sector(file->track, 16);
|
||||
intfstream_read(file->track->stream, buffer, sizeof(buffer));
|
||||
|
||||
/* the directory_record starts at 156 bytes into the sector.
|
||||
* the sector containing the root directory contents is a
|
||||
* 3 byte value that is 2 bytes into the directory_record. */
|
||||
offset = 156 + 2;
|
||||
sector = buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16);
|
||||
}
|
||||
|
||||
/* process the contents of the directory */
|
||||
cdfs_seek_track_sector(file->track, sector);
|
||||
intfstream_read(file->track->stream, buffer, sizeof(buffer));
|
||||
|
||||
path_length = strlen(path);
|
||||
tmp = buffer;
|
||||
|
||||
while (tmp < buffer + sizeof(buffer))
|
||||
{
|
||||
/* The first byte of the record is the length of
|
||||
* the record - if 0, we reached the end of the data */
|
||||
if (!*tmp)
|
||||
break;
|
||||
|
||||
/* filename is 33 bytes into the record and
|
||||
* the format is "FILENAME;version" or "DIRECTORY" */
|
||||
if ((tmp[33 + path_length] == ';' || tmp[33 + path_length] == '\0') &&
|
||||
strncasecmp((const char*)(tmp + 33), path, path_length) == 0)
|
||||
{
|
||||
/* the file size is in bytes 10-13 of the record */
|
||||
file->size = tmp[10] | (tmp[11] << 8)
|
||||
| (tmp[12] << 16) | (tmp[13] << 24);
|
||||
|
||||
/* the file contents are in the sector identified
|
||||
* in bytes 2-4 of the record */
|
||||
sector = tmp[2] | (tmp[3] << 8) | (tmp[4] << 16);
|
||||
return sector;
|
||||
}
|
||||
|
||||
/* the first byte of the record is the length of the record */
|
||||
tmp += tmp[0];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cdfs_open_file(cdfs_file_t* file, cdfs_track_t* track, const char* path)
|
||||
{
|
||||
if (!file || !track)
|
||||
return 0;
|
||||
|
||||
memset(file, 0, sizeof(*file));
|
||||
|
||||
file->track = track;
|
||||
file->current_sector = -1;
|
||||
|
||||
if (path)
|
||||
file->first_sector = cdfs_find_file(file, path);
|
||||
else if (file->track->stream_sector_size)
|
||||
{
|
||||
file->first_sector = 0;
|
||||
file->size = (intfstream_get_size(
|
||||
file->track->stream) / file->track->stream_sector_size)
|
||||
* 2048;
|
||||
}
|
||||
else
|
||||
file->first_sector = -1;
|
||||
|
||||
return (file->first_sector >= 0);
|
||||
}
|
||||
|
||||
int64_t cdfs_read_file(cdfs_file_t* file, void* buffer, uint64_t len)
|
||||
{
|
||||
int bytes_read = 0;
|
||||
|
||||
if (!file || file->first_sector < 0 || !buffer)
|
||||
return 0;
|
||||
|
||||
if (len > file->size - file->pos)
|
||||
len = file->size - file->pos;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (file->sector_buffer_valid)
|
||||
{
|
||||
size_t remaining = 2048 - file->current_sector_offset;
|
||||
if (remaining > 0)
|
||||
{
|
||||
if (remaining >= len)
|
||||
{
|
||||
memcpy(buffer,
|
||||
&file->sector_buffer[file->current_sector_offset],
|
||||
(size_t)len);
|
||||
file->current_sector_offset += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
memcpy(buffer,
|
||||
&file->sector_buffer[file->current_sector_offset], remaining);
|
||||
buffer = (char*)buffer + remaining;
|
||||
bytes_read += remaining;
|
||||
len -= remaining;
|
||||
|
||||
file->current_sector_offset += remaining;
|
||||
}
|
||||
|
||||
++file->current_sector;
|
||||
file->current_sector_offset = 0;
|
||||
file->sector_buffer_valid = 0;
|
||||
}
|
||||
else if (file->current_sector < file->first_sector)
|
||||
{
|
||||
file->current_sector = file->first_sector;
|
||||
file->current_sector_offset = 0;
|
||||
}
|
||||
|
||||
while (len >= 2048)
|
||||
{
|
||||
cdfs_seek_track_sector(file->track, file->current_sector);
|
||||
intfstream_read(file->track->stream, buffer, 2048);
|
||||
|
||||
buffer = (char*)buffer + 2048;
|
||||
bytes_read += 2048;
|
||||
|
||||
++file->current_sector;
|
||||
|
||||
len -= 2048;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
cdfs_seek_track_sector(file->track, file->current_sector);
|
||||
intfstream_read(file->track->stream, file->sector_buffer, 2048);
|
||||
memcpy(buffer, file->sector_buffer, (size_t)len);
|
||||
file->current_sector_offset = len;
|
||||
file->sector_buffer_valid = 1;
|
||||
|
||||
bytes_read += len;
|
||||
}
|
||||
|
||||
file->pos += bytes_read;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
void cdfs_close_file(cdfs_file_t* file)
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
/* not really anything to do here, just
|
||||
* clear out the first_sector so read() won't do anything */
|
||||
file->first_sector = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t cdfs_get_size(cdfs_file_t* file)
|
||||
{
|
||||
if (!file || file->first_sector < 0)
|
||||
return 0;
|
||||
|
||||
return file->size;
|
||||
}
|
||||
|
||||
int64_t cdfs_tell(cdfs_file_t* file)
|
||||
{
|
||||
if (!file || file->first_sector < 0)
|
||||
return -1;
|
||||
|
||||
return file->pos;
|
||||
}
|
||||
|
||||
int64_t cdfs_seek(cdfs_file_t* file, int64_t offset, int whence)
|
||||
{
|
||||
int64_t new_pos;
|
||||
int new_sector;
|
||||
|
||||
if (!file || file->first_sector < 0)
|
||||
return -1;
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
new_pos = offset;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
new_pos = file->pos + offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
new_pos = file->size - offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (new_pos < 0)
|
||||
return -1;
|
||||
else if (new_pos > file->size)
|
||||
return -1;
|
||||
|
||||
file->pos = (unsigned int)new_pos;
|
||||
file->current_sector_offset = file->pos % 2048;
|
||||
|
||||
new_sector = file->pos / 2048;
|
||||
if (new_sector != file->current_sector)
|
||||
{
|
||||
file->current_sector = new_sector;
|
||||
file->sector_buffer_valid = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdfs_skip_spaces(const char** ptr)
|
||||
{
|
||||
while (**ptr && (**ptr == ' ' || **ptr == '\t'))
|
||||
++(*ptr);
|
||||
}
|
||||
|
||||
static cdfs_track_t* cdfs_wrap_stream(
|
||||
intfstream_t* stream, unsigned first_sector_offset, unsigned first_sector_index)
|
||||
{
|
||||
cdfs_track_t* track = NULL;
|
||||
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
track = (cdfs_track_t*)
|
||||
calloc(1, sizeof(*track));
|
||||
track->stream = stream;
|
||||
track->first_sector_offset = first_sector_offset;
|
||||
track->first_sector_index = first_sector_index;
|
||||
|
||||
cdfs_determine_sector_size(track);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
static cdfs_track_t* cdfs_open_cue_track(
|
||||
const char* path, unsigned int track_index)
|
||||
{
|
||||
char* cue = NULL;
|
||||
const char* line = NULL;
|
||||
int found_track = 0;
|
||||
char current_track_path[PATH_MAX_LENGTH] = {0};
|
||||
char track_path[PATH_MAX_LENGTH] = {0};
|
||||
unsigned int sector_size = 0;
|
||||
unsigned int previous_sector_size = 0;
|
||||
unsigned int previous_index_sector_offset= 0;
|
||||
unsigned int track_offset = 0;
|
||||
intfstream_t *cue_stream = intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
int64_t stream_size = intfstream_get_size(cue_stream);
|
||||
char *cue_contents = (char*)malloc((size_t)(stream_size + 1));
|
||||
cdfs_track_t* track = NULL;
|
||||
|
||||
if (!cue_contents)
|
||||
{
|
||||
intfstream_close(cue_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
intfstream_read(cue_stream, cue_contents, stream_size);
|
||||
intfstream_close(cue_stream);
|
||||
|
||||
cue_contents[stream_size] = '\0';
|
||||
|
||||
cue = cue_contents;
|
||||
while (*cue)
|
||||
{
|
||||
cdfs_skip_spaces((const char**)&cue);
|
||||
line = cue;
|
||||
|
||||
while (*cue && *cue != '\n')
|
||||
++cue;
|
||||
if (*cue)
|
||||
*cue++ = '\0';
|
||||
|
||||
if (!strncasecmp(line, "FILE", 4))
|
||||
{
|
||||
const char *file = line + 4;
|
||||
cdfs_skip_spaces(&file);
|
||||
|
||||
if (file[0])
|
||||
{
|
||||
const char *file_end = cue - 1;
|
||||
while (file_end > file && *file_end != ' ' && *file_end != '\t')
|
||||
--file_end;
|
||||
|
||||
if (file[0] == '"' && file_end[-1] == '"')
|
||||
{
|
||||
++file;
|
||||
--file_end;
|
||||
}
|
||||
|
||||
memcpy(current_track_path, file, file_end - file);
|
||||
current_track_path[file_end - file] = '\0';
|
||||
}
|
||||
|
||||
previous_sector_size = 0;
|
||||
previous_index_sector_offset = 0;
|
||||
track_offset = 0;
|
||||
}
|
||||
else if (!strncasecmp(line, "TRACK", 5))
|
||||
{
|
||||
char *ptr = NULL;
|
||||
unsigned track_number = 0;
|
||||
const char *track = line + 5;
|
||||
cdfs_skip_spaces(&track);
|
||||
|
||||
track_number = (unsigned)strtol(track, &ptr, 10);
|
||||
while (*track && *track != ' ' && *track != '\n')
|
||||
++track;
|
||||
|
||||
previous_sector_size = sector_size;
|
||||
|
||||
cdfs_skip_spaces(&track);
|
||||
|
||||
if (!strncasecmp(track, "MODE", 4))
|
||||
{
|
||||
/* track_index = 0 means find the first data track */
|
||||
if (!track_index || track_index == track_number)
|
||||
found_track = track_number;
|
||||
|
||||
sector_size = atoi(track + 6);
|
||||
}
|
||||
else /* assume AUDIO */
|
||||
sector_size = 2352;
|
||||
}
|
||||
else if (!strncasecmp(line, "INDEX", 5))
|
||||
{
|
||||
unsigned min = 0, sec = 0, frame = 0;
|
||||
unsigned index_number = 0;
|
||||
unsigned sector_offset;
|
||||
const char *index = line + 5;
|
||||
|
||||
cdfs_skip_spaces(&index);
|
||||
sscanf(index, "%u", &index_number);
|
||||
while (*index && *index != ' ' && *index != '\n')
|
||||
++index;
|
||||
cdfs_skip_spaces(&index);
|
||||
|
||||
sscanf(index, "%u:%u:%u", &min, &sec, &frame);
|
||||
sector_offset = ((min * 60) + sec) * 75 + frame;
|
||||
sector_offset -= previous_index_sector_offset;
|
||||
track_offset += sector_offset * previous_sector_size;
|
||||
previous_sector_size = sector_size;
|
||||
previous_index_sector_offset += sector_offset;
|
||||
|
||||
if (found_track && index_number == 1)
|
||||
{
|
||||
if ( strstr(current_track_path, "/") ||
|
||||
strstr(current_track_path, "\\"))
|
||||
strncpy(track_path, current_track_path, sizeof(track_path));
|
||||
else
|
||||
{
|
||||
fill_pathname_basedir(track_path, path, sizeof(track_path));
|
||||
strlcat(track_path, current_track_path, sizeof(track_path));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(cue_contents);
|
||||
|
||||
if (string_is_empty(track_path))
|
||||
return NULL;
|
||||
|
||||
/* NOTE: previous_index_sector_offset will only be valid if all tracks are in the same BIN file.
|
||||
* Otherwise, we need to determine how many tracks are in each previous BIN file, which is not
|
||||
* stored in the CUE file. This will affect cdfs_get_first_sector, which luckily isn't used much. */
|
||||
track = cdfs_wrap_stream(intfstream_open_file(
|
||||
track_path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE), track_offset, previous_index_sector_offset);
|
||||
|
||||
if (track && track->stream_sector_size == 0)
|
||||
{
|
||||
track->stream_sector_size = sector_size;
|
||||
|
||||
if (sector_size == 2352)
|
||||
track->stream_sector_header_size = 16;
|
||||
else if (sector_size == 2336)
|
||||
track->stream_sector_header_size = 8;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
static cdfs_track_t* cdfs_open_chd_track(const char* path, int32_t track_index)
|
||||
{
|
||||
intfstream_t* intf_stream;
|
||||
cdfs_track_t* track;
|
||||
|
||||
intf_stream = intfstream_open_chd_track(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE,
|
||||
track_index);
|
||||
if (!intf_stream)
|
||||
return NULL;
|
||||
|
||||
track = cdfs_wrap_stream(intf_stream,
|
||||
intfstream_get_offset_to_start(intf_stream),
|
||||
intfstream_get_first_sector(intf_stream));
|
||||
|
||||
if (track && track->stream_sector_header_size == 0)
|
||||
{
|
||||
track->stream_sector_size = intfstream_get_frame_size(intf_stream);
|
||||
|
||||
if (track->stream_sector_size == 2352)
|
||||
track->stream_sector_header_size = 16;
|
||||
else if (track->stream_sector_size == 2336)
|
||||
track->stream_sector_header_size = 8;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct cdfs_track_t* cdfs_open_track(const char* path,
|
||||
unsigned int track_index)
|
||||
{
|
||||
const char* ext = path_get_extension(path);
|
||||
|
||||
if (string_is_equal_noncase(ext, "cue"))
|
||||
return cdfs_open_cue_track(path, track_index);
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(ext, "chd"))
|
||||
return cdfs_open_chd_track(path, track_index);
|
||||
#endif
|
||||
|
||||
/* if opening track 1, try opening as a raw track */
|
||||
if (track_index == 1)
|
||||
return cdfs_open_raw_track(path);
|
||||
|
||||
/* unsupported file type */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct cdfs_track_t* cdfs_open_data_track(const char* path)
|
||||
{
|
||||
const char* ext = path_get_extension(path);
|
||||
|
||||
if (string_is_equal_noncase(ext, "cue"))
|
||||
return cdfs_open_cue_track(path, 0);
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(ext, "chd"))
|
||||
return cdfs_open_chd_track(path, CHDSTREAM_TRACK_PRIMARY);
|
||||
#endif
|
||||
|
||||
/* unsupported file type - try opening as a raw track */
|
||||
return cdfs_open_raw_track(path);
|
||||
}
|
||||
|
||||
cdfs_track_t* cdfs_open_raw_track(const char* path)
|
||||
{
|
||||
const char* ext = path_get_extension(path);
|
||||
cdfs_track_t* track = NULL;
|
||||
|
||||
if ( string_is_equal_noncase(ext, "bin") ||
|
||||
string_is_equal_noncase(ext, "iso"))
|
||||
{
|
||||
intfstream_t* file = intfstream_open_file(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
track = cdfs_wrap_stream(file, 0, 0);
|
||||
if (track != NULL && track->stream_sector_size == 0)
|
||||
{
|
||||
cdfs_determine_sector_size_from_file_size(track);
|
||||
if (track->stream_sector_size == 0)
|
||||
{
|
||||
cdfs_close_track(track);
|
||||
track = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
void cdfs_close_track(cdfs_track_t* track)
|
||||
{
|
||||
if (track)
|
||||
{
|
||||
if (track->stream)
|
||||
{
|
||||
intfstream_close(track->stream);
|
||||
free(track->stream);
|
||||
}
|
||||
|
||||
free(track);
|
||||
}
|
||||
}
|
||||
333
src/minarch/libretro-common/formats/image_texture.c
Normal file
333
src/minarch/libretro-common/formats/image_texture.c
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
/* Copyright (C) 2010-2020 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>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
enum image_type_enum image_texture_get_type(const char *path)
|
||||
{
|
||||
/* We are comparing against a fixed list of file
|
||||
* extensions, the longest (jpeg) being 4 characters
|
||||
* in length. We therefore only need to extract the first
|
||||
* 5 characters from the extension of the input path
|
||||
* to correctly validate a match */
|
||||
const char *ext = NULL;
|
||||
char ext_lower[6];
|
||||
|
||||
ext_lower[0] = '\0';
|
||||
|
||||
if (string_is_empty(path))
|
||||
return IMAGE_TYPE_NONE;
|
||||
|
||||
/* Get file extension */
|
||||
ext = strrchr(path, '.');
|
||||
|
||||
if (!ext || (*(++ext) == '\0'))
|
||||
return IMAGE_TYPE_NONE;
|
||||
|
||||
/* Copy and convert to lower case */
|
||||
strlcpy(ext_lower, ext, sizeof(ext_lower));
|
||||
string_to_lower(ext_lower);
|
||||
|
||||
#ifdef HAVE_RPNG
|
||||
if (string_is_equal(ext_lower, "png"))
|
||||
return IMAGE_TYPE_PNG;
|
||||
#endif
|
||||
#ifdef HAVE_RJPEG
|
||||
if (string_is_equal(ext_lower, "jpg") ||
|
||||
string_is_equal(ext_lower, "jpeg"))
|
||||
return IMAGE_TYPE_JPEG;
|
||||
#endif
|
||||
#ifdef HAVE_RBMP
|
||||
if (string_is_equal(ext_lower, "bmp"))
|
||||
return IMAGE_TYPE_BMP;
|
||||
#endif
|
||||
#ifdef HAVE_RTGA
|
||||
if (string_is_equal(ext_lower, "tga"))
|
||||
return IMAGE_TYPE_TGA;
|
||||
#endif
|
||||
|
||||
return IMAGE_TYPE_NONE;
|
||||
}
|
||||
|
||||
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);
|
||||
/* Explicitly cast these to uint32_t to prevent
|
||||
* ASAN runtime error: left shift of 255 by 24 places
|
||||
* cannot be represented in type 'int' */
|
||||
pixels[i] = ((uint32_t)a << a_shift) |
|
||||
((uint32_t)r << r_shift) |
|
||||
((uint32_t)g << g_shift) |
|
||||
((uint32_t)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, len);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool image_texture_load_buffer(struct texture_image *out_img,
|
||||
enum image_type_enum type, void *buffer, size_t buffer_len)
|
||||
{
|
||||
unsigned r_shift, g_shift, b_shift, a_shift;
|
||||
image_texture_set_color_shifts(&r_shift, &g_shift, &b_shift,
|
||||
&a_shift, out_img);
|
||||
|
||||
if (type != IMAGE_TYPE_NONE)
|
||||
{
|
||||
if (image_texture_load_internal(
|
||||
type, buffer, buffer_len, out_img,
|
||||
a_shift, r_shift, g_shift, b_shift))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
out_img->supports_rgba = false;
|
||||
out_img->pixels = NULL;
|
||||
out_img->width = 0;
|
||||
out_img->height = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 image_type_enum type = image_texture_get_type(path);
|
||||
|
||||
image_texture_set_color_shifts(&r_shift, &g_shift, &b_shift,
|
||||
&a_shift, out_img);
|
||||
|
||||
if (type != IMAGE_TYPE_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(
|
||||
type,
|
||||
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;
|
||||
}
|
||||
285
src/minarch/libretro-common/formats/image_transfer.c
Normal file
285
src/minarch/libretro-common/formats/image_transfer.c
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/* Copyright (C) 2010-2020 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 <boolean.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,
|
||||
size_t len)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case IMAGE_TYPE_PNG:
|
||||
#ifdef HAVE_RPNG
|
||||
rpng_set_buf_ptr((rpng_t*)data, (uint8_t*)ptr, len);
|
||||
#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
|
||||
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;
|
||||
}
|
||||
2635
src/minarch/libretro-common/formats/jpeg/rjpeg.c
Normal file
2635
src/minarch/libretro-common/formats/jpeg/rjpeg.c
Normal file
File diff suppressed because it is too large
Load diff
1447
src/minarch/libretro-common/formats/json/rjson.c
Normal file
1447
src/minarch/libretro-common/formats/json/rjson.c
Normal file
File diff suppressed because it is too large
Load diff
119
src/minarch/libretro-common/formats/libchdr/libchdr_bitstream.c
Normal file
119
src/minarch/libretro-common/formats/libchdr/libchdr_bitstream.c
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/* 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;
|
||||
}
|
||||
417
src/minarch/libretro-common/formats/libchdr/libchdr_cdrom.c
Normal file
417
src/minarch/libretro-common/formats/libchdr/libchdr_cdrom.c
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
/* license:BSD-3-Clause
|
||||
* copyright-holders:Aaron Giles
|
||||
***************************************************************************
|
||||
|
||||
cdrom.c
|
||||
|
||||
Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this
|
||||
|
||||
****************************************************************************
|
||||
|
||||
IMPORTANT:
|
||||
"physical" block addresses are the actual addresses on the emulated CD.
|
||||
"chd" block addresses are the block addresses in the CHD file.
|
||||
Because we pad each track to a 4-frame boundary, these addressing
|
||||
schemes will differ after track 1!
|
||||
|
||||
***************************************************************************/
|
||||
#ifdef WANT_RAW_DATA_SECTOR
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
|
||||
#include <libchdr/cdrom.h>
|
||||
|
||||
/***************************************************************************
|
||||
DEBUGGING
|
||||
***************************************************************************/
|
||||
|
||||
/** @brief The verbose. */
|
||||
#define VERBOSE (0)
|
||||
#if VERBOSE
|
||||
|
||||
/**
|
||||
* @def LOG(x) do
|
||||
*
|
||||
* @brief A macro that defines log.
|
||||
*
|
||||
* @param x The void to process.
|
||||
*/
|
||||
|
||||
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
|
||||
|
||||
/**
|
||||
* @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
|
||||
*
|
||||
* @brief Logerrors the given text.
|
||||
*
|
||||
* @param text The text.
|
||||
*
|
||||
* @return A CLIB_DECL.
|
||||
*/
|
||||
|
||||
void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
|
||||
#else
|
||||
|
||||
/**
|
||||
* @def LOG(x);
|
||||
*
|
||||
* @brief A macro that defines log.
|
||||
*
|
||||
* @param x The void to process.
|
||||
*/
|
||||
|
||||
#define LOG(x)
|
||||
#endif
|
||||
|
||||
/***************************************************************************
|
||||
CONSTANTS
|
||||
***************************************************************************/
|
||||
|
||||
/** @brief offset within sector. */
|
||||
#define SYNC_OFFSET 0x000
|
||||
/** @brief 12 bytes. */
|
||||
#define SYNC_NUM_BYTES 12
|
||||
|
||||
/** @brief offset within sector. */
|
||||
#define MODE_OFFSET 0x00f
|
||||
|
||||
/** @brief offset within sector. */
|
||||
#define ECC_P_OFFSET 0x81c
|
||||
/** @brief 2 lots of 86. */
|
||||
#define ECC_P_NUM_BYTES 86
|
||||
/** @brief 24 bytes each. */
|
||||
#define ECC_P_COMP 24
|
||||
|
||||
/** @brief The ECC q offset. */
|
||||
#define ECC_Q_OFFSET (ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES)
|
||||
/** @brief 2 lots of 52. */
|
||||
#define ECC_Q_NUM_BYTES 52
|
||||
/** @brief 43 bytes each. */
|
||||
#define ECC_Q_COMP 43
|
||||
|
||||
/**
|
||||
* @brief -------------------------------------------------
|
||||
* ECC lookup tables pre-calculated tables for ECC data calcs
|
||||
* -------------------------------------------------.
|
||||
*/
|
||||
|
||||
static const uint8_t ecclow[256] =
|
||||
{
|
||||
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
|
||||
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
|
||||
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
|
||||
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
|
||||
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
|
||||
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
|
||||
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
|
||||
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
|
||||
0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0x0d, 0x0f, 0x09, 0x0b, 0x05, 0x07, 0x01, 0x03,
|
||||
0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23,
|
||||
0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43,
|
||||
0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63,
|
||||
0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83,
|
||||
0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3,
|
||||
0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3,
|
||||
0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3
|
||||
};
|
||||
|
||||
/** @brief The ecchigh[ 256]. */
|
||||
static const uint8_t ecchigh[256] =
|
||||
{
|
||||
0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05,
|
||||
0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe,
|
||||
0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee,
|
||||
0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15,
|
||||
0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce,
|
||||
0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35,
|
||||
0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25,
|
||||
0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde,
|
||||
0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e,
|
||||
0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75,
|
||||
0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65,
|
||||
0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e,
|
||||
0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45,
|
||||
0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe,
|
||||
0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae,
|
||||
0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief -------------------------------------------------
|
||||
* poffsets - each row represents the addresses used to calculate a byte of the ECC P
|
||||
* data 86 (*2) ECC P bytes, 24 values represented by each
|
||||
* -------------------------------------------------.
|
||||
*/
|
||||
|
||||
static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
|
||||
{
|
||||
{ 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba },
|
||||
{ 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb },
|
||||
{ 0x002,0x058,0x0ae,0x104,0x15a,0x1b0,0x206,0x25c,0x2b2,0x308,0x35e,0x3b4,0x40a,0x460,0x4b6,0x50c,0x562,0x5b8,0x60e,0x664,0x6ba,0x710,0x766,0x7bc },
|
||||
{ 0x003,0x059,0x0af,0x105,0x15b,0x1b1,0x207,0x25d,0x2b3,0x309,0x35f,0x3b5,0x40b,0x461,0x4b7,0x50d,0x563,0x5b9,0x60f,0x665,0x6bb,0x711,0x767,0x7bd },
|
||||
{ 0x004,0x05a,0x0b0,0x106,0x15c,0x1b2,0x208,0x25e,0x2b4,0x30a,0x360,0x3b6,0x40c,0x462,0x4b8,0x50e,0x564,0x5ba,0x610,0x666,0x6bc,0x712,0x768,0x7be },
|
||||
{ 0x005,0x05b,0x0b1,0x107,0x15d,0x1b3,0x209,0x25f,0x2b5,0x30b,0x361,0x3b7,0x40d,0x463,0x4b9,0x50f,0x565,0x5bb,0x611,0x667,0x6bd,0x713,0x769,0x7bf },
|
||||
{ 0x006,0x05c,0x0b2,0x108,0x15e,0x1b4,0x20a,0x260,0x2b6,0x30c,0x362,0x3b8,0x40e,0x464,0x4ba,0x510,0x566,0x5bc,0x612,0x668,0x6be,0x714,0x76a,0x7c0 },
|
||||
{ 0x007,0x05d,0x0b3,0x109,0x15f,0x1b5,0x20b,0x261,0x2b7,0x30d,0x363,0x3b9,0x40f,0x465,0x4bb,0x511,0x567,0x5bd,0x613,0x669,0x6bf,0x715,0x76b,0x7c1 },
|
||||
{ 0x008,0x05e,0x0b4,0x10a,0x160,0x1b6,0x20c,0x262,0x2b8,0x30e,0x364,0x3ba,0x410,0x466,0x4bc,0x512,0x568,0x5be,0x614,0x66a,0x6c0,0x716,0x76c,0x7c2 },
|
||||
{ 0x009,0x05f,0x0b5,0x10b,0x161,0x1b7,0x20d,0x263,0x2b9,0x30f,0x365,0x3bb,0x411,0x467,0x4bd,0x513,0x569,0x5bf,0x615,0x66b,0x6c1,0x717,0x76d,0x7c3 },
|
||||
{ 0x00a,0x060,0x0b6,0x10c,0x162,0x1b8,0x20e,0x264,0x2ba,0x310,0x366,0x3bc,0x412,0x468,0x4be,0x514,0x56a,0x5c0,0x616,0x66c,0x6c2,0x718,0x76e,0x7c4 },
|
||||
{ 0x00b,0x061,0x0b7,0x10d,0x163,0x1b9,0x20f,0x265,0x2bb,0x311,0x367,0x3bd,0x413,0x469,0x4bf,0x515,0x56b,0x5c1,0x617,0x66d,0x6c3,0x719,0x76f,0x7c5 },
|
||||
{ 0x00c,0x062,0x0b8,0x10e,0x164,0x1ba,0x210,0x266,0x2bc,0x312,0x368,0x3be,0x414,0x46a,0x4c0,0x516,0x56c,0x5c2,0x618,0x66e,0x6c4,0x71a,0x770,0x7c6 },
|
||||
{ 0x00d,0x063,0x0b9,0x10f,0x165,0x1bb,0x211,0x267,0x2bd,0x313,0x369,0x3bf,0x415,0x46b,0x4c1,0x517,0x56d,0x5c3,0x619,0x66f,0x6c5,0x71b,0x771,0x7c7 },
|
||||
{ 0x00e,0x064,0x0ba,0x110,0x166,0x1bc,0x212,0x268,0x2be,0x314,0x36a,0x3c0,0x416,0x46c,0x4c2,0x518,0x56e,0x5c4,0x61a,0x670,0x6c6,0x71c,0x772,0x7c8 },
|
||||
{ 0x00f,0x065,0x0bb,0x111,0x167,0x1bd,0x213,0x269,0x2bf,0x315,0x36b,0x3c1,0x417,0x46d,0x4c3,0x519,0x56f,0x5c5,0x61b,0x671,0x6c7,0x71d,0x773,0x7c9 },
|
||||
{ 0x010,0x066,0x0bc,0x112,0x168,0x1be,0x214,0x26a,0x2c0,0x316,0x36c,0x3c2,0x418,0x46e,0x4c4,0x51a,0x570,0x5c6,0x61c,0x672,0x6c8,0x71e,0x774,0x7ca },
|
||||
{ 0x011,0x067,0x0bd,0x113,0x169,0x1bf,0x215,0x26b,0x2c1,0x317,0x36d,0x3c3,0x419,0x46f,0x4c5,0x51b,0x571,0x5c7,0x61d,0x673,0x6c9,0x71f,0x775,0x7cb },
|
||||
{ 0x012,0x068,0x0be,0x114,0x16a,0x1c0,0x216,0x26c,0x2c2,0x318,0x36e,0x3c4,0x41a,0x470,0x4c6,0x51c,0x572,0x5c8,0x61e,0x674,0x6ca,0x720,0x776,0x7cc },
|
||||
{ 0x013,0x069,0x0bf,0x115,0x16b,0x1c1,0x217,0x26d,0x2c3,0x319,0x36f,0x3c5,0x41b,0x471,0x4c7,0x51d,0x573,0x5c9,0x61f,0x675,0x6cb,0x721,0x777,0x7cd },
|
||||
{ 0x014,0x06a,0x0c0,0x116,0x16c,0x1c2,0x218,0x26e,0x2c4,0x31a,0x370,0x3c6,0x41c,0x472,0x4c8,0x51e,0x574,0x5ca,0x620,0x676,0x6cc,0x722,0x778,0x7ce },
|
||||
{ 0x015,0x06b,0x0c1,0x117,0x16d,0x1c3,0x219,0x26f,0x2c5,0x31b,0x371,0x3c7,0x41d,0x473,0x4c9,0x51f,0x575,0x5cb,0x621,0x677,0x6cd,0x723,0x779,0x7cf },
|
||||
{ 0x016,0x06c,0x0c2,0x118,0x16e,0x1c4,0x21a,0x270,0x2c6,0x31c,0x372,0x3c8,0x41e,0x474,0x4ca,0x520,0x576,0x5cc,0x622,0x678,0x6ce,0x724,0x77a,0x7d0 },
|
||||
{ 0x017,0x06d,0x0c3,0x119,0x16f,0x1c5,0x21b,0x271,0x2c7,0x31d,0x373,0x3c9,0x41f,0x475,0x4cb,0x521,0x577,0x5cd,0x623,0x679,0x6cf,0x725,0x77b,0x7d1 },
|
||||
{ 0x018,0x06e,0x0c4,0x11a,0x170,0x1c6,0x21c,0x272,0x2c8,0x31e,0x374,0x3ca,0x420,0x476,0x4cc,0x522,0x578,0x5ce,0x624,0x67a,0x6d0,0x726,0x77c,0x7d2 },
|
||||
{ 0x019,0x06f,0x0c5,0x11b,0x171,0x1c7,0x21d,0x273,0x2c9,0x31f,0x375,0x3cb,0x421,0x477,0x4cd,0x523,0x579,0x5cf,0x625,0x67b,0x6d1,0x727,0x77d,0x7d3 },
|
||||
{ 0x01a,0x070,0x0c6,0x11c,0x172,0x1c8,0x21e,0x274,0x2ca,0x320,0x376,0x3cc,0x422,0x478,0x4ce,0x524,0x57a,0x5d0,0x626,0x67c,0x6d2,0x728,0x77e,0x7d4 },
|
||||
{ 0x01b,0x071,0x0c7,0x11d,0x173,0x1c9,0x21f,0x275,0x2cb,0x321,0x377,0x3cd,0x423,0x479,0x4cf,0x525,0x57b,0x5d1,0x627,0x67d,0x6d3,0x729,0x77f,0x7d5 },
|
||||
{ 0x01c,0x072,0x0c8,0x11e,0x174,0x1ca,0x220,0x276,0x2cc,0x322,0x378,0x3ce,0x424,0x47a,0x4d0,0x526,0x57c,0x5d2,0x628,0x67e,0x6d4,0x72a,0x780,0x7d6 },
|
||||
{ 0x01d,0x073,0x0c9,0x11f,0x175,0x1cb,0x221,0x277,0x2cd,0x323,0x379,0x3cf,0x425,0x47b,0x4d1,0x527,0x57d,0x5d3,0x629,0x67f,0x6d5,0x72b,0x781,0x7d7 },
|
||||
{ 0x01e,0x074,0x0ca,0x120,0x176,0x1cc,0x222,0x278,0x2ce,0x324,0x37a,0x3d0,0x426,0x47c,0x4d2,0x528,0x57e,0x5d4,0x62a,0x680,0x6d6,0x72c,0x782,0x7d8 },
|
||||
{ 0x01f,0x075,0x0cb,0x121,0x177,0x1cd,0x223,0x279,0x2cf,0x325,0x37b,0x3d1,0x427,0x47d,0x4d3,0x529,0x57f,0x5d5,0x62b,0x681,0x6d7,0x72d,0x783,0x7d9 },
|
||||
{ 0x020,0x076,0x0cc,0x122,0x178,0x1ce,0x224,0x27a,0x2d0,0x326,0x37c,0x3d2,0x428,0x47e,0x4d4,0x52a,0x580,0x5d6,0x62c,0x682,0x6d8,0x72e,0x784,0x7da },
|
||||
{ 0x021,0x077,0x0cd,0x123,0x179,0x1cf,0x225,0x27b,0x2d1,0x327,0x37d,0x3d3,0x429,0x47f,0x4d5,0x52b,0x581,0x5d7,0x62d,0x683,0x6d9,0x72f,0x785,0x7db },
|
||||
{ 0x022,0x078,0x0ce,0x124,0x17a,0x1d0,0x226,0x27c,0x2d2,0x328,0x37e,0x3d4,0x42a,0x480,0x4d6,0x52c,0x582,0x5d8,0x62e,0x684,0x6da,0x730,0x786,0x7dc },
|
||||
{ 0x023,0x079,0x0cf,0x125,0x17b,0x1d1,0x227,0x27d,0x2d3,0x329,0x37f,0x3d5,0x42b,0x481,0x4d7,0x52d,0x583,0x5d9,0x62f,0x685,0x6db,0x731,0x787,0x7dd },
|
||||
{ 0x024,0x07a,0x0d0,0x126,0x17c,0x1d2,0x228,0x27e,0x2d4,0x32a,0x380,0x3d6,0x42c,0x482,0x4d8,0x52e,0x584,0x5da,0x630,0x686,0x6dc,0x732,0x788,0x7de },
|
||||
{ 0x025,0x07b,0x0d1,0x127,0x17d,0x1d3,0x229,0x27f,0x2d5,0x32b,0x381,0x3d7,0x42d,0x483,0x4d9,0x52f,0x585,0x5db,0x631,0x687,0x6dd,0x733,0x789,0x7df },
|
||||
{ 0x026,0x07c,0x0d2,0x128,0x17e,0x1d4,0x22a,0x280,0x2d6,0x32c,0x382,0x3d8,0x42e,0x484,0x4da,0x530,0x586,0x5dc,0x632,0x688,0x6de,0x734,0x78a,0x7e0 },
|
||||
{ 0x027,0x07d,0x0d3,0x129,0x17f,0x1d5,0x22b,0x281,0x2d7,0x32d,0x383,0x3d9,0x42f,0x485,0x4db,0x531,0x587,0x5dd,0x633,0x689,0x6df,0x735,0x78b,0x7e1 },
|
||||
{ 0x028,0x07e,0x0d4,0x12a,0x180,0x1d6,0x22c,0x282,0x2d8,0x32e,0x384,0x3da,0x430,0x486,0x4dc,0x532,0x588,0x5de,0x634,0x68a,0x6e0,0x736,0x78c,0x7e2 },
|
||||
{ 0x029,0x07f,0x0d5,0x12b,0x181,0x1d7,0x22d,0x283,0x2d9,0x32f,0x385,0x3db,0x431,0x487,0x4dd,0x533,0x589,0x5df,0x635,0x68b,0x6e1,0x737,0x78d,0x7e3 },
|
||||
{ 0x02a,0x080,0x0d6,0x12c,0x182,0x1d8,0x22e,0x284,0x2da,0x330,0x386,0x3dc,0x432,0x488,0x4de,0x534,0x58a,0x5e0,0x636,0x68c,0x6e2,0x738,0x78e,0x7e4 },
|
||||
{ 0x02b,0x081,0x0d7,0x12d,0x183,0x1d9,0x22f,0x285,0x2db,0x331,0x387,0x3dd,0x433,0x489,0x4df,0x535,0x58b,0x5e1,0x637,0x68d,0x6e3,0x739,0x78f,0x7e5 },
|
||||
{ 0x02c,0x082,0x0d8,0x12e,0x184,0x1da,0x230,0x286,0x2dc,0x332,0x388,0x3de,0x434,0x48a,0x4e0,0x536,0x58c,0x5e2,0x638,0x68e,0x6e4,0x73a,0x790,0x7e6 },
|
||||
{ 0x02d,0x083,0x0d9,0x12f,0x185,0x1db,0x231,0x287,0x2dd,0x333,0x389,0x3df,0x435,0x48b,0x4e1,0x537,0x58d,0x5e3,0x639,0x68f,0x6e5,0x73b,0x791,0x7e7 },
|
||||
{ 0x02e,0x084,0x0da,0x130,0x186,0x1dc,0x232,0x288,0x2de,0x334,0x38a,0x3e0,0x436,0x48c,0x4e2,0x538,0x58e,0x5e4,0x63a,0x690,0x6e6,0x73c,0x792,0x7e8 },
|
||||
{ 0x02f,0x085,0x0db,0x131,0x187,0x1dd,0x233,0x289,0x2df,0x335,0x38b,0x3e1,0x437,0x48d,0x4e3,0x539,0x58f,0x5e5,0x63b,0x691,0x6e7,0x73d,0x793,0x7e9 },
|
||||
{ 0x030,0x086,0x0dc,0x132,0x188,0x1de,0x234,0x28a,0x2e0,0x336,0x38c,0x3e2,0x438,0x48e,0x4e4,0x53a,0x590,0x5e6,0x63c,0x692,0x6e8,0x73e,0x794,0x7ea },
|
||||
{ 0x031,0x087,0x0dd,0x133,0x189,0x1df,0x235,0x28b,0x2e1,0x337,0x38d,0x3e3,0x439,0x48f,0x4e5,0x53b,0x591,0x5e7,0x63d,0x693,0x6e9,0x73f,0x795,0x7eb },
|
||||
{ 0x032,0x088,0x0de,0x134,0x18a,0x1e0,0x236,0x28c,0x2e2,0x338,0x38e,0x3e4,0x43a,0x490,0x4e6,0x53c,0x592,0x5e8,0x63e,0x694,0x6ea,0x740,0x796,0x7ec },
|
||||
{ 0x033,0x089,0x0df,0x135,0x18b,0x1e1,0x237,0x28d,0x2e3,0x339,0x38f,0x3e5,0x43b,0x491,0x4e7,0x53d,0x593,0x5e9,0x63f,0x695,0x6eb,0x741,0x797,0x7ed },
|
||||
{ 0x034,0x08a,0x0e0,0x136,0x18c,0x1e2,0x238,0x28e,0x2e4,0x33a,0x390,0x3e6,0x43c,0x492,0x4e8,0x53e,0x594,0x5ea,0x640,0x696,0x6ec,0x742,0x798,0x7ee },
|
||||
{ 0x035,0x08b,0x0e1,0x137,0x18d,0x1e3,0x239,0x28f,0x2e5,0x33b,0x391,0x3e7,0x43d,0x493,0x4e9,0x53f,0x595,0x5eb,0x641,0x697,0x6ed,0x743,0x799,0x7ef },
|
||||
{ 0x036,0x08c,0x0e2,0x138,0x18e,0x1e4,0x23a,0x290,0x2e6,0x33c,0x392,0x3e8,0x43e,0x494,0x4ea,0x540,0x596,0x5ec,0x642,0x698,0x6ee,0x744,0x79a,0x7f0 },
|
||||
{ 0x037,0x08d,0x0e3,0x139,0x18f,0x1e5,0x23b,0x291,0x2e7,0x33d,0x393,0x3e9,0x43f,0x495,0x4eb,0x541,0x597,0x5ed,0x643,0x699,0x6ef,0x745,0x79b,0x7f1 },
|
||||
{ 0x038,0x08e,0x0e4,0x13a,0x190,0x1e6,0x23c,0x292,0x2e8,0x33e,0x394,0x3ea,0x440,0x496,0x4ec,0x542,0x598,0x5ee,0x644,0x69a,0x6f0,0x746,0x79c,0x7f2 },
|
||||
{ 0x039,0x08f,0x0e5,0x13b,0x191,0x1e7,0x23d,0x293,0x2e9,0x33f,0x395,0x3eb,0x441,0x497,0x4ed,0x543,0x599,0x5ef,0x645,0x69b,0x6f1,0x747,0x79d,0x7f3 },
|
||||
{ 0x03a,0x090,0x0e6,0x13c,0x192,0x1e8,0x23e,0x294,0x2ea,0x340,0x396,0x3ec,0x442,0x498,0x4ee,0x544,0x59a,0x5f0,0x646,0x69c,0x6f2,0x748,0x79e,0x7f4 },
|
||||
{ 0x03b,0x091,0x0e7,0x13d,0x193,0x1e9,0x23f,0x295,0x2eb,0x341,0x397,0x3ed,0x443,0x499,0x4ef,0x545,0x59b,0x5f1,0x647,0x69d,0x6f3,0x749,0x79f,0x7f5 },
|
||||
{ 0x03c,0x092,0x0e8,0x13e,0x194,0x1ea,0x240,0x296,0x2ec,0x342,0x398,0x3ee,0x444,0x49a,0x4f0,0x546,0x59c,0x5f2,0x648,0x69e,0x6f4,0x74a,0x7a0,0x7f6 },
|
||||
{ 0x03d,0x093,0x0e9,0x13f,0x195,0x1eb,0x241,0x297,0x2ed,0x343,0x399,0x3ef,0x445,0x49b,0x4f1,0x547,0x59d,0x5f3,0x649,0x69f,0x6f5,0x74b,0x7a1,0x7f7 },
|
||||
{ 0x03e,0x094,0x0ea,0x140,0x196,0x1ec,0x242,0x298,0x2ee,0x344,0x39a,0x3f0,0x446,0x49c,0x4f2,0x548,0x59e,0x5f4,0x64a,0x6a0,0x6f6,0x74c,0x7a2,0x7f8 },
|
||||
{ 0x03f,0x095,0x0eb,0x141,0x197,0x1ed,0x243,0x299,0x2ef,0x345,0x39b,0x3f1,0x447,0x49d,0x4f3,0x549,0x59f,0x5f5,0x64b,0x6a1,0x6f7,0x74d,0x7a3,0x7f9 },
|
||||
{ 0x040,0x096,0x0ec,0x142,0x198,0x1ee,0x244,0x29a,0x2f0,0x346,0x39c,0x3f2,0x448,0x49e,0x4f4,0x54a,0x5a0,0x5f6,0x64c,0x6a2,0x6f8,0x74e,0x7a4,0x7fa },
|
||||
{ 0x041,0x097,0x0ed,0x143,0x199,0x1ef,0x245,0x29b,0x2f1,0x347,0x39d,0x3f3,0x449,0x49f,0x4f5,0x54b,0x5a1,0x5f7,0x64d,0x6a3,0x6f9,0x74f,0x7a5,0x7fb },
|
||||
{ 0x042,0x098,0x0ee,0x144,0x19a,0x1f0,0x246,0x29c,0x2f2,0x348,0x39e,0x3f4,0x44a,0x4a0,0x4f6,0x54c,0x5a2,0x5f8,0x64e,0x6a4,0x6fa,0x750,0x7a6,0x7fc },
|
||||
{ 0x043,0x099,0x0ef,0x145,0x19b,0x1f1,0x247,0x29d,0x2f3,0x349,0x39f,0x3f5,0x44b,0x4a1,0x4f7,0x54d,0x5a3,0x5f9,0x64f,0x6a5,0x6fb,0x751,0x7a7,0x7fd },
|
||||
{ 0x044,0x09a,0x0f0,0x146,0x19c,0x1f2,0x248,0x29e,0x2f4,0x34a,0x3a0,0x3f6,0x44c,0x4a2,0x4f8,0x54e,0x5a4,0x5fa,0x650,0x6a6,0x6fc,0x752,0x7a8,0x7fe },
|
||||
{ 0x045,0x09b,0x0f1,0x147,0x19d,0x1f3,0x249,0x29f,0x2f5,0x34b,0x3a1,0x3f7,0x44d,0x4a3,0x4f9,0x54f,0x5a5,0x5fb,0x651,0x6a7,0x6fd,0x753,0x7a9,0x7ff },
|
||||
{ 0x046,0x09c,0x0f2,0x148,0x19e,0x1f4,0x24a,0x2a0,0x2f6,0x34c,0x3a2,0x3f8,0x44e,0x4a4,0x4fa,0x550,0x5a6,0x5fc,0x652,0x6a8,0x6fe,0x754,0x7aa,0x800 },
|
||||
{ 0x047,0x09d,0x0f3,0x149,0x19f,0x1f5,0x24b,0x2a1,0x2f7,0x34d,0x3a3,0x3f9,0x44f,0x4a5,0x4fb,0x551,0x5a7,0x5fd,0x653,0x6a9,0x6ff,0x755,0x7ab,0x801 },
|
||||
{ 0x048,0x09e,0x0f4,0x14a,0x1a0,0x1f6,0x24c,0x2a2,0x2f8,0x34e,0x3a4,0x3fa,0x450,0x4a6,0x4fc,0x552,0x5a8,0x5fe,0x654,0x6aa,0x700,0x756,0x7ac,0x802 },
|
||||
{ 0x049,0x09f,0x0f5,0x14b,0x1a1,0x1f7,0x24d,0x2a3,0x2f9,0x34f,0x3a5,0x3fb,0x451,0x4a7,0x4fd,0x553,0x5a9,0x5ff,0x655,0x6ab,0x701,0x757,0x7ad,0x803 },
|
||||
{ 0x04a,0x0a0,0x0f6,0x14c,0x1a2,0x1f8,0x24e,0x2a4,0x2fa,0x350,0x3a6,0x3fc,0x452,0x4a8,0x4fe,0x554,0x5aa,0x600,0x656,0x6ac,0x702,0x758,0x7ae,0x804 },
|
||||
{ 0x04b,0x0a1,0x0f7,0x14d,0x1a3,0x1f9,0x24f,0x2a5,0x2fb,0x351,0x3a7,0x3fd,0x453,0x4a9,0x4ff,0x555,0x5ab,0x601,0x657,0x6ad,0x703,0x759,0x7af,0x805 },
|
||||
{ 0x04c,0x0a2,0x0f8,0x14e,0x1a4,0x1fa,0x250,0x2a6,0x2fc,0x352,0x3a8,0x3fe,0x454,0x4aa,0x500,0x556,0x5ac,0x602,0x658,0x6ae,0x704,0x75a,0x7b0,0x806 },
|
||||
{ 0x04d,0x0a3,0x0f9,0x14f,0x1a5,0x1fb,0x251,0x2a7,0x2fd,0x353,0x3a9,0x3ff,0x455,0x4ab,0x501,0x557,0x5ad,0x603,0x659,0x6af,0x705,0x75b,0x7b1,0x807 },
|
||||
{ 0x04e,0x0a4,0x0fa,0x150,0x1a6,0x1fc,0x252,0x2a8,0x2fe,0x354,0x3aa,0x400,0x456,0x4ac,0x502,0x558,0x5ae,0x604,0x65a,0x6b0,0x706,0x75c,0x7b2,0x808 },
|
||||
{ 0x04f,0x0a5,0x0fb,0x151,0x1a7,0x1fd,0x253,0x2a9,0x2ff,0x355,0x3ab,0x401,0x457,0x4ad,0x503,0x559,0x5af,0x605,0x65b,0x6b1,0x707,0x75d,0x7b3,0x809 },
|
||||
{ 0x050,0x0a6,0x0fc,0x152,0x1a8,0x1fe,0x254,0x2aa,0x300,0x356,0x3ac,0x402,0x458,0x4ae,0x504,0x55a,0x5b0,0x606,0x65c,0x6b2,0x708,0x75e,0x7b4,0x80a },
|
||||
{ 0x051,0x0a7,0x0fd,0x153,0x1a9,0x1ff,0x255,0x2ab,0x301,0x357,0x3ad,0x403,0x459,0x4af,0x505,0x55b,0x5b1,0x607,0x65d,0x6b3,0x709,0x75f,0x7b5,0x80b },
|
||||
{ 0x052,0x0a8,0x0fe,0x154,0x1aa,0x200,0x256,0x2ac,0x302,0x358,0x3ae,0x404,0x45a,0x4b0,0x506,0x55c,0x5b2,0x608,0x65e,0x6b4,0x70a,0x760,0x7b6,0x80c },
|
||||
{ 0x053,0x0a9,0x0ff,0x155,0x1ab,0x201,0x257,0x2ad,0x303,0x359,0x3af,0x405,0x45b,0x4b1,0x507,0x55d,0x5b3,0x609,0x65f,0x6b5,0x70b,0x761,0x7b7,0x80d },
|
||||
{ 0x054,0x0aa,0x100,0x156,0x1ac,0x202,0x258,0x2ae,0x304,0x35a,0x3b0,0x406,0x45c,0x4b2,0x508,0x55e,0x5b4,0x60a,0x660,0x6b6,0x70c,0x762,0x7b8,0x80e },
|
||||
{ 0x055,0x0ab,0x101,0x157,0x1ad,0x203,0x259,0x2af,0x305,0x35b,0x3b1,0x407,0x45d,0x4b3,0x509,0x55f,0x5b5,0x60b,0x661,0x6b7,0x70d,0x763,0x7b9,0x80f }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief -------------------------------------------------
|
||||
* qoffsets - each row represents the addresses used to calculate a byte of the ECC Q
|
||||
* data 52 (*2) ECC Q bytes, 43 values represented by each
|
||||
* -------------------------------------------------.
|
||||
*/
|
||||
|
||||
static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
|
||||
{
|
||||
{ 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 },
|
||||
{ 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 },
|
||||
{ 0x056,0x0ae,0x106,0x15e,0x1b6,0x20e,0x266,0x2be,0x316,0x36e,0x3c6,0x41e,0x476,0x4ce,0x526,0x57e,0x5d6,0x62e,0x686,0x6de,0x736,0x78e,0x7e6,0x83e,0x896,0x032,0x08a,0x0e2,0x13a,0x192,0x1ea,0x242,0x29a,0x2f2,0x34a,0x3a2,0x3fa,0x452,0x4aa,0x502,0x55a,0x5b2,0x60a },
|
||||
{ 0x057,0x0af,0x107,0x15f,0x1b7,0x20f,0x267,0x2bf,0x317,0x36f,0x3c7,0x41f,0x477,0x4cf,0x527,0x57f,0x5d7,0x62f,0x687,0x6df,0x737,0x78f,0x7e7,0x83f,0x897,0x033,0x08b,0x0e3,0x13b,0x193,0x1eb,0x243,0x29b,0x2f3,0x34b,0x3a3,0x3fb,0x453,0x4ab,0x503,0x55b,0x5b3,0x60b },
|
||||
{ 0x0ac,0x104,0x15c,0x1b4,0x20c,0x264,0x2bc,0x314,0x36c,0x3c4,0x41c,0x474,0x4cc,0x524,0x57c,0x5d4,0x62c,0x684,0x6dc,0x734,0x78c,0x7e4,0x83c,0x894,0x030,0x088,0x0e0,0x138,0x190,0x1e8,0x240,0x298,0x2f0,0x348,0x3a0,0x3f8,0x450,0x4a8,0x500,0x558,0x5b0,0x608,0x660 },
|
||||
{ 0x0ad,0x105,0x15d,0x1b5,0x20d,0x265,0x2bd,0x315,0x36d,0x3c5,0x41d,0x475,0x4cd,0x525,0x57d,0x5d5,0x62d,0x685,0x6dd,0x735,0x78d,0x7e5,0x83d,0x895,0x031,0x089,0x0e1,0x139,0x191,0x1e9,0x241,0x299,0x2f1,0x349,0x3a1,0x3f9,0x451,0x4a9,0x501,0x559,0x5b1,0x609,0x661 },
|
||||
{ 0x102,0x15a,0x1b2,0x20a,0x262,0x2ba,0x312,0x36a,0x3c2,0x41a,0x472,0x4ca,0x522,0x57a,0x5d2,0x62a,0x682,0x6da,0x732,0x78a,0x7e2,0x83a,0x892,0x02e,0x086,0x0de,0x136,0x18e,0x1e6,0x23e,0x296,0x2ee,0x346,0x39e,0x3f6,0x44e,0x4a6,0x4fe,0x556,0x5ae,0x606,0x65e,0x6b6 },
|
||||
{ 0x103,0x15b,0x1b3,0x20b,0x263,0x2bb,0x313,0x36b,0x3c3,0x41b,0x473,0x4cb,0x523,0x57b,0x5d3,0x62b,0x683,0x6db,0x733,0x78b,0x7e3,0x83b,0x893,0x02f,0x087,0x0df,0x137,0x18f,0x1e7,0x23f,0x297,0x2ef,0x347,0x39f,0x3f7,0x44f,0x4a7,0x4ff,0x557,0x5af,0x607,0x65f,0x6b7 },
|
||||
{ 0x158,0x1b0,0x208,0x260,0x2b8,0x310,0x368,0x3c0,0x418,0x470,0x4c8,0x520,0x578,0x5d0,0x628,0x680,0x6d8,0x730,0x788,0x7e0,0x838,0x890,0x02c,0x084,0x0dc,0x134,0x18c,0x1e4,0x23c,0x294,0x2ec,0x344,0x39c,0x3f4,0x44c,0x4a4,0x4fc,0x554,0x5ac,0x604,0x65c,0x6b4,0x70c },
|
||||
{ 0x159,0x1b1,0x209,0x261,0x2b9,0x311,0x369,0x3c1,0x419,0x471,0x4c9,0x521,0x579,0x5d1,0x629,0x681,0x6d9,0x731,0x789,0x7e1,0x839,0x891,0x02d,0x085,0x0dd,0x135,0x18d,0x1e5,0x23d,0x295,0x2ed,0x345,0x39d,0x3f5,0x44d,0x4a5,0x4fd,0x555,0x5ad,0x605,0x65d,0x6b5,0x70d },
|
||||
{ 0x1ae,0x206,0x25e,0x2b6,0x30e,0x366,0x3be,0x416,0x46e,0x4c6,0x51e,0x576,0x5ce,0x626,0x67e,0x6d6,0x72e,0x786,0x7de,0x836,0x88e,0x02a,0x082,0x0da,0x132,0x18a,0x1e2,0x23a,0x292,0x2ea,0x342,0x39a,0x3f2,0x44a,0x4a2,0x4fa,0x552,0x5aa,0x602,0x65a,0x6b2,0x70a,0x762 },
|
||||
{ 0x1af,0x207,0x25f,0x2b7,0x30f,0x367,0x3bf,0x417,0x46f,0x4c7,0x51f,0x577,0x5cf,0x627,0x67f,0x6d7,0x72f,0x787,0x7df,0x837,0x88f,0x02b,0x083,0x0db,0x133,0x18b,0x1e3,0x23b,0x293,0x2eb,0x343,0x39b,0x3f3,0x44b,0x4a3,0x4fb,0x553,0x5ab,0x603,0x65b,0x6b3,0x70b,0x763 },
|
||||
{ 0x204,0x25c,0x2b4,0x30c,0x364,0x3bc,0x414,0x46c,0x4c4,0x51c,0x574,0x5cc,0x624,0x67c,0x6d4,0x72c,0x784,0x7dc,0x834,0x88c,0x028,0x080,0x0d8,0x130,0x188,0x1e0,0x238,0x290,0x2e8,0x340,0x398,0x3f0,0x448,0x4a0,0x4f8,0x550,0x5a8,0x600,0x658,0x6b0,0x708,0x760,0x7b8 },
|
||||
{ 0x205,0x25d,0x2b5,0x30d,0x365,0x3bd,0x415,0x46d,0x4c5,0x51d,0x575,0x5cd,0x625,0x67d,0x6d5,0x72d,0x785,0x7dd,0x835,0x88d,0x029,0x081,0x0d9,0x131,0x189,0x1e1,0x239,0x291,0x2e9,0x341,0x399,0x3f1,0x449,0x4a1,0x4f9,0x551,0x5a9,0x601,0x659,0x6b1,0x709,0x761,0x7b9 },
|
||||
{ 0x25a,0x2b2,0x30a,0x362,0x3ba,0x412,0x46a,0x4c2,0x51a,0x572,0x5ca,0x622,0x67a,0x6d2,0x72a,0x782,0x7da,0x832,0x88a,0x026,0x07e,0x0d6,0x12e,0x186,0x1de,0x236,0x28e,0x2e6,0x33e,0x396,0x3ee,0x446,0x49e,0x4f6,0x54e,0x5a6,0x5fe,0x656,0x6ae,0x706,0x75e,0x7b6,0x80e },
|
||||
{ 0x25b,0x2b3,0x30b,0x363,0x3bb,0x413,0x46b,0x4c3,0x51b,0x573,0x5cb,0x623,0x67b,0x6d3,0x72b,0x783,0x7db,0x833,0x88b,0x027,0x07f,0x0d7,0x12f,0x187,0x1df,0x237,0x28f,0x2e7,0x33f,0x397,0x3ef,0x447,0x49f,0x4f7,0x54f,0x5a7,0x5ff,0x657,0x6af,0x707,0x75f,0x7b7,0x80f },
|
||||
{ 0x2b0,0x308,0x360,0x3b8,0x410,0x468,0x4c0,0x518,0x570,0x5c8,0x620,0x678,0x6d0,0x728,0x780,0x7d8,0x830,0x888,0x024,0x07c,0x0d4,0x12c,0x184,0x1dc,0x234,0x28c,0x2e4,0x33c,0x394,0x3ec,0x444,0x49c,0x4f4,0x54c,0x5a4,0x5fc,0x654,0x6ac,0x704,0x75c,0x7b4,0x80c,0x864 },
|
||||
{ 0x2b1,0x309,0x361,0x3b9,0x411,0x469,0x4c1,0x519,0x571,0x5c9,0x621,0x679,0x6d1,0x729,0x781,0x7d9,0x831,0x889,0x025,0x07d,0x0d5,0x12d,0x185,0x1dd,0x235,0x28d,0x2e5,0x33d,0x395,0x3ed,0x445,0x49d,0x4f5,0x54d,0x5a5,0x5fd,0x655,0x6ad,0x705,0x75d,0x7b5,0x80d,0x865 },
|
||||
{ 0x306,0x35e,0x3b6,0x40e,0x466,0x4be,0x516,0x56e,0x5c6,0x61e,0x676,0x6ce,0x726,0x77e,0x7d6,0x82e,0x886,0x022,0x07a,0x0d2,0x12a,0x182,0x1da,0x232,0x28a,0x2e2,0x33a,0x392,0x3ea,0x442,0x49a,0x4f2,0x54a,0x5a2,0x5fa,0x652,0x6aa,0x702,0x75a,0x7b2,0x80a,0x862,0x8ba },
|
||||
{ 0x307,0x35f,0x3b7,0x40f,0x467,0x4bf,0x517,0x56f,0x5c7,0x61f,0x677,0x6cf,0x727,0x77f,0x7d7,0x82f,0x887,0x023,0x07b,0x0d3,0x12b,0x183,0x1db,0x233,0x28b,0x2e3,0x33b,0x393,0x3eb,0x443,0x49b,0x4f3,0x54b,0x5a3,0x5fb,0x653,0x6ab,0x703,0x75b,0x7b3,0x80b,0x863,0x8bb },
|
||||
{ 0x35c,0x3b4,0x40c,0x464,0x4bc,0x514,0x56c,0x5c4,0x61c,0x674,0x6cc,0x724,0x77c,0x7d4,0x82c,0x884,0x020,0x078,0x0d0,0x128,0x180,0x1d8,0x230,0x288,0x2e0,0x338,0x390,0x3e8,0x440,0x498,0x4f0,0x548,0x5a0,0x5f8,0x650,0x6a8,0x700,0x758,0x7b0,0x808,0x860,0x8b8,0x054 },
|
||||
{ 0x35d,0x3b5,0x40d,0x465,0x4bd,0x515,0x56d,0x5c5,0x61d,0x675,0x6cd,0x725,0x77d,0x7d5,0x82d,0x885,0x021,0x079,0x0d1,0x129,0x181,0x1d9,0x231,0x289,0x2e1,0x339,0x391,0x3e9,0x441,0x499,0x4f1,0x549,0x5a1,0x5f9,0x651,0x6a9,0x701,0x759,0x7b1,0x809,0x861,0x8b9,0x055 },
|
||||
{ 0x3b2,0x40a,0x462,0x4ba,0x512,0x56a,0x5c2,0x61a,0x672,0x6ca,0x722,0x77a,0x7d2,0x82a,0x882,0x01e,0x076,0x0ce,0x126,0x17e,0x1d6,0x22e,0x286,0x2de,0x336,0x38e,0x3e6,0x43e,0x496,0x4ee,0x546,0x59e,0x5f6,0x64e,0x6a6,0x6fe,0x756,0x7ae,0x806,0x85e,0x8b6,0x052,0x0aa },
|
||||
{ 0x3b3,0x40b,0x463,0x4bb,0x513,0x56b,0x5c3,0x61b,0x673,0x6cb,0x723,0x77b,0x7d3,0x82b,0x883,0x01f,0x077,0x0cf,0x127,0x17f,0x1d7,0x22f,0x287,0x2df,0x337,0x38f,0x3e7,0x43f,0x497,0x4ef,0x547,0x59f,0x5f7,0x64f,0x6a7,0x6ff,0x757,0x7af,0x807,0x85f,0x8b7,0x053,0x0ab },
|
||||
{ 0x408,0x460,0x4b8,0x510,0x568,0x5c0,0x618,0x670,0x6c8,0x720,0x778,0x7d0,0x828,0x880,0x01c,0x074,0x0cc,0x124,0x17c,0x1d4,0x22c,0x284,0x2dc,0x334,0x38c,0x3e4,0x43c,0x494,0x4ec,0x544,0x59c,0x5f4,0x64c,0x6a4,0x6fc,0x754,0x7ac,0x804,0x85c,0x8b4,0x050,0x0a8,0x100 },
|
||||
{ 0x409,0x461,0x4b9,0x511,0x569,0x5c1,0x619,0x671,0x6c9,0x721,0x779,0x7d1,0x829,0x881,0x01d,0x075,0x0cd,0x125,0x17d,0x1d5,0x22d,0x285,0x2dd,0x335,0x38d,0x3e5,0x43d,0x495,0x4ed,0x545,0x59d,0x5f5,0x64d,0x6a5,0x6fd,0x755,0x7ad,0x805,0x85d,0x8b5,0x051,0x0a9,0x101 },
|
||||
{ 0x45e,0x4b6,0x50e,0x566,0x5be,0x616,0x66e,0x6c6,0x71e,0x776,0x7ce,0x826,0x87e,0x01a,0x072,0x0ca,0x122,0x17a,0x1d2,0x22a,0x282,0x2da,0x332,0x38a,0x3e2,0x43a,0x492,0x4ea,0x542,0x59a,0x5f2,0x64a,0x6a2,0x6fa,0x752,0x7aa,0x802,0x85a,0x8b2,0x04e,0x0a6,0x0fe,0x156 },
|
||||
{ 0x45f,0x4b7,0x50f,0x567,0x5bf,0x617,0x66f,0x6c7,0x71f,0x777,0x7cf,0x827,0x87f,0x01b,0x073,0x0cb,0x123,0x17b,0x1d3,0x22b,0x283,0x2db,0x333,0x38b,0x3e3,0x43b,0x493,0x4eb,0x543,0x59b,0x5f3,0x64b,0x6a3,0x6fb,0x753,0x7ab,0x803,0x85b,0x8b3,0x04f,0x0a7,0x0ff,0x157 },
|
||||
{ 0x4b4,0x50c,0x564,0x5bc,0x614,0x66c,0x6c4,0x71c,0x774,0x7cc,0x824,0x87c,0x018,0x070,0x0c8,0x120,0x178,0x1d0,0x228,0x280,0x2d8,0x330,0x388,0x3e0,0x438,0x490,0x4e8,0x540,0x598,0x5f0,0x648,0x6a0,0x6f8,0x750,0x7a8,0x800,0x858,0x8b0,0x04c,0x0a4,0x0fc,0x154,0x1ac },
|
||||
{ 0x4b5,0x50d,0x565,0x5bd,0x615,0x66d,0x6c5,0x71d,0x775,0x7cd,0x825,0x87d,0x019,0x071,0x0c9,0x121,0x179,0x1d1,0x229,0x281,0x2d9,0x331,0x389,0x3e1,0x439,0x491,0x4e9,0x541,0x599,0x5f1,0x649,0x6a1,0x6f9,0x751,0x7a9,0x801,0x859,0x8b1,0x04d,0x0a5,0x0fd,0x155,0x1ad },
|
||||
{ 0x50a,0x562,0x5ba,0x612,0x66a,0x6c2,0x71a,0x772,0x7ca,0x822,0x87a,0x016,0x06e,0x0c6,0x11e,0x176,0x1ce,0x226,0x27e,0x2d6,0x32e,0x386,0x3de,0x436,0x48e,0x4e6,0x53e,0x596,0x5ee,0x646,0x69e,0x6f6,0x74e,0x7a6,0x7fe,0x856,0x8ae,0x04a,0x0a2,0x0fa,0x152,0x1aa,0x202 },
|
||||
{ 0x50b,0x563,0x5bb,0x613,0x66b,0x6c3,0x71b,0x773,0x7cb,0x823,0x87b,0x017,0x06f,0x0c7,0x11f,0x177,0x1cf,0x227,0x27f,0x2d7,0x32f,0x387,0x3df,0x437,0x48f,0x4e7,0x53f,0x597,0x5ef,0x647,0x69f,0x6f7,0x74f,0x7a7,0x7ff,0x857,0x8af,0x04b,0x0a3,0x0fb,0x153,0x1ab,0x203 },
|
||||
{ 0x560,0x5b8,0x610,0x668,0x6c0,0x718,0x770,0x7c8,0x820,0x878,0x014,0x06c,0x0c4,0x11c,0x174,0x1cc,0x224,0x27c,0x2d4,0x32c,0x384,0x3dc,0x434,0x48c,0x4e4,0x53c,0x594,0x5ec,0x644,0x69c,0x6f4,0x74c,0x7a4,0x7fc,0x854,0x8ac,0x048,0x0a0,0x0f8,0x150,0x1a8,0x200,0x258 },
|
||||
{ 0x561,0x5b9,0x611,0x669,0x6c1,0x719,0x771,0x7c9,0x821,0x879,0x015,0x06d,0x0c5,0x11d,0x175,0x1cd,0x225,0x27d,0x2d5,0x32d,0x385,0x3dd,0x435,0x48d,0x4e5,0x53d,0x595,0x5ed,0x645,0x69d,0x6f5,0x74d,0x7a5,0x7fd,0x855,0x8ad,0x049,0x0a1,0x0f9,0x151,0x1a9,0x201,0x259 },
|
||||
{ 0x5b6,0x60e,0x666,0x6be,0x716,0x76e,0x7c6,0x81e,0x876,0x012,0x06a,0x0c2,0x11a,0x172,0x1ca,0x222,0x27a,0x2d2,0x32a,0x382,0x3da,0x432,0x48a,0x4e2,0x53a,0x592,0x5ea,0x642,0x69a,0x6f2,0x74a,0x7a2,0x7fa,0x852,0x8aa,0x046,0x09e,0x0f6,0x14e,0x1a6,0x1fe,0x256,0x2ae },
|
||||
{ 0x5b7,0x60f,0x667,0x6bf,0x717,0x76f,0x7c7,0x81f,0x877,0x013,0x06b,0x0c3,0x11b,0x173,0x1cb,0x223,0x27b,0x2d3,0x32b,0x383,0x3db,0x433,0x48b,0x4e3,0x53b,0x593,0x5eb,0x643,0x69b,0x6f3,0x74b,0x7a3,0x7fb,0x853,0x8ab,0x047,0x09f,0x0f7,0x14f,0x1a7,0x1ff,0x257,0x2af },
|
||||
{ 0x60c,0x664,0x6bc,0x714,0x76c,0x7c4,0x81c,0x874,0x010,0x068,0x0c0,0x118,0x170,0x1c8,0x220,0x278,0x2d0,0x328,0x380,0x3d8,0x430,0x488,0x4e0,0x538,0x590,0x5e8,0x640,0x698,0x6f0,0x748,0x7a0,0x7f8,0x850,0x8a8,0x044,0x09c,0x0f4,0x14c,0x1a4,0x1fc,0x254,0x2ac,0x304 },
|
||||
{ 0x60d,0x665,0x6bd,0x715,0x76d,0x7c5,0x81d,0x875,0x011,0x069,0x0c1,0x119,0x171,0x1c9,0x221,0x279,0x2d1,0x329,0x381,0x3d9,0x431,0x489,0x4e1,0x539,0x591,0x5e9,0x641,0x699,0x6f1,0x749,0x7a1,0x7f9,0x851,0x8a9,0x045,0x09d,0x0f5,0x14d,0x1a5,0x1fd,0x255,0x2ad,0x305 },
|
||||
{ 0x662,0x6ba,0x712,0x76a,0x7c2,0x81a,0x872,0x00e,0x066,0x0be,0x116,0x16e,0x1c6,0x21e,0x276,0x2ce,0x326,0x37e,0x3d6,0x42e,0x486,0x4de,0x536,0x58e,0x5e6,0x63e,0x696,0x6ee,0x746,0x79e,0x7f6,0x84e,0x8a6,0x042,0x09a,0x0f2,0x14a,0x1a2,0x1fa,0x252,0x2aa,0x302,0x35a },
|
||||
{ 0x663,0x6bb,0x713,0x76b,0x7c3,0x81b,0x873,0x00f,0x067,0x0bf,0x117,0x16f,0x1c7,0x21f,0x277,0x2cf,0x327,0x37f,0x3d7,0x42f,0x487,0x4df,0x537,0x58f,0x5e7,0x63f,0x697,0x6ef,0x747,0x79f,0x7f7,0x84f,0x8a7,0x043,0x09b,0x0f3,0x14b,0x1a3,0x1fb,0x253,0x2ab,0x303,0x35b },
|
||||
{ 0x6b8,0x710,0x768,0x7c0,0x818,0x870,0x00c,0x064,0x0bc,0x114,0x16c,0x1c4,0x21c,0x274,0x2cc,0x324,0x37c,0x3d4,0x42c,0x484,0x4dc,0x534,0x58c,0x5e4,0x63c,0x694,0x6ec,0x744,0x79c,0x7f4,0x84c,0x8a4,0x040,0x098,0x0f0,0x148,0x1a0,0x1f8,0x250,0x2a8,0x300,0x358,0x3b0 },
|
||||
{ 0x6b9,0x711,0x769,0x7c1,0x819,0x871,0x00d,0x065,0x0bd,0x115,0x16d,0x1c5,0x21d,0x275,0x2cd,0x325,0x37d,0x3d5,0x42d,0x485,0x4dd,0x535,0x58d,0x5e5,0x63d,0x695,0x6ed,0x745,0x79d,0x7f5,0x84d,0x8a5,0x041,0x099,0x0f1,0x149,0x1a1,0x1f9,0x251,0x2a9,0x301,0x359,0x3b1 },
|
||||
{ 0x70e,0x766,0x7be,0x816,0x86e,0x00a,0x062,0x0ba,0x112,0x16a,0x1c2,0x21a,0x272,0x2ca,0x322,0x37a,0x3d2,0x42a,0x482,0x4da,0x532,0x58a,0x5e2,0x63a,0x692,0x6ea,0x742,0x79a,0x7f2,0x84a,0x8a2,0x03e,0x096,0x0ee,0x146,0x19e,0x1f6,0x24e,0x2a6,0x2fe,0x356,0x3ae,0x406 },
|
||||
{ 0x70f,0x767,0x7bf,0x817,0x86f,0x00b,0x063,0x0bb,0x113,0x16b,0x1c3,0x21b,0x273,0x2cb,0x323,0x37b,0x3d3,0x42b,0x483,0x4db,0x533,0x58b,0x5e3,0x63b,0x693,0x6eb,0x743,0x79b,0x7f3,0x84b,0x8a3,0x03f,0x097,0x0ef,0x147,0x19f,0x1f7,0x24f,0x2a7,0x2ff,0x357,0x3af,0x407 },
|
||||
{ 0x764,0x7bc,0x814,0x86c,0x008,0x060,0x0b8,0x110,0x168,0x1c0,0x218,0x270,0x2c8,0x320,0x378,0x3d0,0x428,0x480,0x4d8,0x530,0x588,0x5e0,0x638,0x690,0x6e8,0x740,0x798,0x7f0,0x848,0x8a0,0x03c,0x094,0x0ec,0x144,0x19c,0x1f4,0x24c,0x2a4,0x2fc,0x354,0x3ac,0x404,0x45c },
|
||||
{ 0x765,0x7bd,0x815,0x86d,0x009,0x061,0x0b9,0x111,0x169,0x1c1,0x219,0x271,0x2c9,0x321,0x379,0x3d1,0x429,0x481,0x4d9,0x531,0x589,0x5e1,0x639,0x691,0x6e9,0x741,0x799,0x7f1,0x849,0x8a1,0x03d,0x095,0x0ed,0x145,0x19d,0x1f5,0x24d,0x2a5,0x2fd,0x355,0x3ad,0x405,0x45d },
|
||||
{ 0x7ba,0x812,0x86a,0x006,0x05e,0x0b6,0x10e,0x166,0x1be,0x216,0x26e,0x2c6,0x31e,0x376,0x3ce,0x426,0x47e,0x4d6,0x52e,0x586,0x5de,0x636,0x68e,0x6e6,0x73e,0x796,0x7ee,0x846,0x89e,0x03a,0x092,0x0ea,0x142,0x19a,0x1f2,0x24a,0x2a2,0x2fa,0x352,0x3aa,0x402,0x45a,0x4b2 },
|
||||
{ 0x7bb,0x813,0x86b,0x007,0x05f,0x0b7,0x10f,0x167,0x1bf,0x217,0x26f,0x2c7,0x31f,0x377,0x3cf,0x427,0x47f,0x4d7,0x52f,0x587,0x5df,0x637,0x68f,0x6e7,0x73f,0x797,0x7ef,0x847,0x89f,0x03b,0x093,0x0eb,0x143,0x19b,0x1f3,0x24b,0x2a3,0x2fb,0x353,0x3ab,0x403,0x45b,0x4b3 },
|
||||
{ 0x810,0x868,0x004,0x05c,0x0b4,0x10c,0x164,0x1bc,0x214,0x26c,0x2c4,0x31c,0x374,0x3cc,0x424,0x47c,0x4d4,0x52c,0x584,0x5dc,0x634,0x68c,0x6e4,0x73c,0x794,0x7ec,0x844,0x89c,0x038,0x090,0x0e8,0x140,0x198,0x1f0,0x248,0x2a0,0x2f8,0x350,0x3a8,0x400,0x458,0x4b0,0x508 },
|
||||
{ 0x811,0x869,0x005,0x05d,0x0b5,0x10d,0x165,0x1bd,0x215,0x26d,0x2c5,0x31d,0x375,0x3cd,0x425,0x47d,0x4d5,0x52d,0x585,0x5dd,0x635,0x68d,0x6e5,0x73d,0x795,0x7ed,0x845,0x89d,0x039,0x091,0x0e9,0x141,0x199,0x1f1,0x249,0x2a1,0x2f9,0x351,0x3a9,0x401,0x459,0x4b1,0x509 },
|
||||
{ 0x866,0x002,0x05a,0x0b2,0x10a,0x162,0x1ba,0x212,0x26a,0x2c2,0x31a,0x372,0x3ca,0x422,0x47a,0x4d2,0x52a,0x582,0x5da,0x632,0x68a,0x6e2,0x73a,0x792,0x7ea,0x842,0x89a,0x036,0x08e,0x0e6,0x13e,0x196,0x1ee,0x246,0x29e,0x2f6,0x34e,0x3a6,0x3fe,0x456,0x4ae,0x506,0x55e },
|
||||
{ 0x867,0x003,0x05b,0x0b3,0x10b,0x163,0x1bb,0x213,0x26b,0x2c3,0x31b,0x373,0x3cb,0x423,0x47b,0x4d3,0x52b,0x583,0x5db,0x633,0x68b,0x6e3,0x73b,0x793,0x7eb,0x843,0x89b,0x037,0x08f,0x0e7,0x13f,0x197,0x1ef,0x247,0x29f,0x2f7,0x34f,0x3a7,0x3ff,0x457,0x4af,0x507,0x55f }
|
||||
};
|
||||
|
||||
/*-------------------------------------------------
|
||||
* ecc_source_byte - return data from the sector
|
||||
* at the given offset, masking anything
|
||||
* particular to a mode
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
static INLINE uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
|
||||
{
|
||||
/* in mode 2 always treat these as 0 bytes */
|
||||
return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
|
||||
*
|
||||
* @brief -------------------------------------------------
|
||||
* ecc_compute_bytes - calculate an ECC value (P or Q)
|
||||
* -------------------------------------------------.
|
||||
*
|
||||
* @param sector The sector.
|
||||
* @param row The row.
|
||||
* @param rowlen The rowlen.
|
||||
* @param [in,out] val1 The first value.
|
||||
* @param [in,out] val2 The second value.
|
||||
*/
|
||||
|
||||
void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t *val1, uint8_t *val2)
|
||||
{
|
||||
int component;
|
||||
*val1 = *val2 = 0;
|
||||
for (component = 0; component < rowlen; component++)
|
||||
{
|
||||
*val1 ^= ecc_source_byte(sector, row[component]);
|
||||
*val2 ^= ecc_source_byte(sector, row[component]);
|
||||
*val1 = ecclow[*val1];
|
||||
}
|
||||
*val1 = ecchigh[ecclow[*val1] ^ *val2];
|
||||
*val2 ^= *val1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn int ecc_verify(const uint8_t *sector)
|
||||
*
|
||||
* @brief -------------------------------------------------
|
||||
* ecc_verify - verify the P and Q ECC codes in a sector
|
||||
* -------------------------------------------------.
|
||||
*
|
||||
* @param sector The sector.
|
||||
*
|
||||
* @return true if it succeeds, false if it fails.
|
||||
*/
|
||||
|
||||
int ecc_verify(const uint8_t *sector)
|
||||
{
|
||||
int byte;
|
||||
/* first verify P bytes */
|
||||
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
|
||||
{
|
||||
uint8_t val1, val2;
|
||||
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &val1, &val2);
|
||||
if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* then verify Q bytes */
|
||||
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
|
||||
{
|
||||
uint8_t val1, val2;
|
||||
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &val1, &val2);
|
||||
if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void ecc_generate(uint8_t *sector)
|
||||
*
|
||||
* @brief -------------------------------------------------
|
||||
* ecc_generate - generate the P and Q ECC codes for a sector, overwriting any
|
||||
* existing codes
|
||||
* -------------------------------------------------.
|
||||
*
|
||||
* @param [in,out] sector If non-null, the sector.
|
||||
*/
|
||||
|
||||
void ecc_generate(uint8_t *sector)
|
||||
{
|
||||
int byte;
|
||||
/* first verify P bytes */
|
||||
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
|
||||
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, §or[ECC_P_OFFSET + byte], §or[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]);
|
||||
|
||||
/* then verify Q bytes */
|
||||
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
|
||||
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, §or[ECC_Q_OFFSET + byte], §or[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void ecc_clear(uint8_t *sector)
|
||||
*
|
||||
* @brief -------------------------------------------------
|
||||
* ecc_clear - erase the ECC P and Q cods to 0 within a sector
|
||||
* -------------------------------------------------.
|
||||
*
|
||||
* @param [in,out] sector If non-null, the sector.
|
||||
*/
|
||||
|
||||
void ecc_clear(uint8_t *sector)
|
||||
{
|
||||
memset(§or[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
|
||||
memset(§or[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
|
||||
}
|
||||
|
||||
#endif /* WANT_RAW_DATA_SECTOR */
|
||||
1935
src/minarch/libretro-common/formats/libchdr/libchdr_chd.c
Normal file
1935
src/minarch/libretro-common/formats/libchdr/libchdr_chd.c
Normal file
File diff suppressed because it is too large
Load diff
324
src/minarch/libretro-common/formats/libchdr/libchdr_flac.c
Normal file
324
src/minarch/libretro-common/formats/libchdr/libchdr_flac.c
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/* 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 (uint32_t)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 = (uint32_t)*bytes;
|
||||
|
||||
/* copy from primary buffer first */
|
||||
uint32_t outputpos = 0;
|
||||
|
||||
if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length)
|
||||
{
|
||||
uint32_t bytes_to_copy = (uint32_t)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 = (uint32_t)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)
|
||||
{
|
||||
}
|
||||
163
src/minarch/libretro-common/formats/libchdr/libchdr_flac_codec.c
Normal file
163
src/minarch/libretro-common/formats/libchdr/libchdr_flac_codec.c
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/***************************************************************************
|
||||
|
||||
libchdr_flac_codec.c
|
||||
|
||||
MAME Compressed Hunks of Data file format
|
||||
|
||||
****************************************************************************
|
||||
|
||||
Copyright Aaron Giles
|
||||
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.
|
||||
* Neither the name 'MAME' nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY AARON GILES ''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 AARON GILES 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libchdr/chd.h>
|
||||
#include <libchdr/minmax.h>
|
||||
#include <libchdr/cdrom.h>
|
||||
#include <libchdr/flac.h>
|
||||
#include <libchdr/huffman.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
/***************************************************************************
|
||||
* CD FLAC DECOMPRESSOR
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
/*------------------------------------------------------
|
||||
* cdfl_codec_blocksize - return the optimal block size
|
||||
*------------------------------------------------------
|
||||
*/
|
||||
|
||||
static uint32_t cdfl_codec_blocksize(uint32_t bytes)
|
||||
{
|
||||
/* determine FLAC block size, which must be 16-65535
|
||||
* clamp to 2k since that's supposed to be the sweet spot */
|
||||
uint32_t hunkbytes = bytes / 4;
|
||||
while (hunkbytes > 2048)
|
||||
hunkbytes /= 2;
|
||||
return hunkbytes;
|
||||
}
|
||||
|
||||
chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes)
|
||||
{
|
||||
#ifdef WANT_SUBCODE
|
||||
chd_error ret;
|
||||
#endif
|
||||
uint16_t native_endian = 0;
|
||||
cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;
|
||||
|
||||
/* make sure the CHD's hunk size is an even multiple of the frame size */
|
||||
if (hunkbytes % CD_FRAME_SIZE != 0)
|
||||
return CHDERR_CODEC_ERROR;
|
||||
|
||||
cdfl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
|
||||
if (cdfl->buffer == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
|
||||
/* determine whether we want native or swapped samples */
|
||||
*(uint8_t *)(&native_endian) = 1;
|
||||
cdfl->swap_endian = (native_endian & 1);
|
||||
|
||||
#ifdef WANT_SUBCODE
|
||||
/* init zlib inflater */
|
||||
ret = zlib_codec_init(&cdfl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
|
||||
if (ret != CHDERR_NONE)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
/* flac decoder init */
|
||||
flac_decoder_init(&cdfl->decoder);
|
||||
if (cdfl->decoder.decoder == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
void cdfl_codec_free(void *codec)
|
||||
{
|
||||
cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;
|
||||
flac_decoder_free(&cdfl->decoder);
|
||||
#ifdef WANT_SUBCODE
|
||||
zlib_codec_free(&cdfl->subcode_decompressor);
|
||||
#endif
|
||||
if (cdfl->buffer)
|
||||
free(cdfl->buffer);
|
||||
}
|
||||
|
||||
chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
|
||||
{
|
||||
uint32_t framenum;
|
||||
uint8_t *buffer;
|
||||
#ifdef WANT_SUBCODE
|
||||
uint32_t offset;
|
||||
chd_error ret;
|
||||
#endif
|
||||
cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;
|
||||
|
||||
/* reset and decode */
|
||||
uint32_t frames = destlen / CD_FRAME_SIZE;
|
||||
|
||||
if (!flac_decoder_reset(&cdfl->decoder, 44100, 2, cdfl_codec_blocksize(frames * CD_MAX_SECTOR_DATA), src, complen))
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
buffer = &cdfl->buffer[0];
|
||||
if (!flac_decoder_decode_interleaved(&cdfl->decoder, (int16_t *)(buffer), frames * CD_MAX_SECTOR_DATA/4, cdfl->swap_endian))
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
|
||||
#ifdef WANT_SUBCODE
|
||||
/* inflate the subcode data */
|
||||
offset = flac_decoder_finish(&cdfl->decoder);
|
||||
ret = zlib_codec_decompress(&cdfl->subcode_decompressor, src + offset, complen - offset, &cdfl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
|
||||
if (ret != CHDERR_NONE)
|
||||
return ret;
|
||||
#else
|
||||
flac_decoder_finish(&cdfl->decoder);
|
||||
#endif
|
||||
|
||||
/* reassemble the data */
|
||||
for (framenum = 0; framenum < frames; framenum++)
|
||||
{
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE], &cdfl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
|
||||
#ifdef WANT_SUBCODE
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdfl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);
|
||||
#endif
|
||||
}
|
||||
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
563
src/minarch/libretro-common/formats/libchdr/libchdr_huffman.c
Normal file
563
src/minarch/libretro-common/formats/libchdr/libchdr_huffman.c
Normal 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;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
/* set up the entry */
|
||||
lookup_value value = MAKE_LOOKUP(curcode, node->numbits);
|
||||
|
||||
/* fill all matching entries */
|
||||
int shift = decoder->maxbits - node->numbits;
|
||||
lookup_value *dest = &decoder->lookup[node->bits << shift];
|
||||
lookup_value *destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
|
||||
while (dest <= destend)
|
||||
*dest++ = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
366
src/minarch/libretro-common/formats/libchdr/libchdr_lzma.c
Normal file
366
src/minarch/libretro-common/formats/libchdr/libchdr_lzma.c
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/***************************************************************************
|
||||
|
||||
libchdr_lzma_codec.c
|
||||
|
||||
MAME Compressed Hunks of Data file format
|
||||
|
||||
****************************************************************************
|
||||
|
||||
Copyright Aaron Giles
|
||||
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.
|
||||
* Neither the name 'MAME' nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY AARON GILES ''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 AARON GILES 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include <libchdr/chd.h>
|
||||
#include <libchdr/minmax.h>
|
||||
#include <libchdr/cdrom.h>
|
||||
#include <libchdr/lzma.h>
|
||||
#include <libchdr/huffman.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
/***************************************************************************
|
||||
* LZMA ALLOCATOR HELPER
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------
|
||||
* lzma_fast_alloc - fast malloc for lzma, which
|
||||
* allocates and frees memory frequently
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Huge alignment values for possible SIMD optimization by compiler (NEON, SSE, AVX) */
|
||||
#define LZMA_MIN_ALIGNMENT_BITS 512
|
||||
#define LZMA_MIN_ALIGNMENT_BYTES (LZMA_MIN_ALIGNMENT_BITS / 8)
|
||||
|
||||
static void *lzma_fast_alloc(void *p, size_t size)
|
||||
{
|
||||
int scan;
|
||||
uint32_t *addr = NULL;
|
||||
lzma_allocator *codec = (lzma_allocator *)(p);
|
||||
uintptr_t vaddr = 0;
|
||||
|
||||
/* compute the size, rounding to the nearest 1k */
|
||||
size = (size + 0x3ff) & ~0x3ff;
|
||||
|
||||
/* reuse a hunk if we can */
|
||||
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
|
||||
{
|
||||
uint32_t *ptr = codec->allocptr[scan];
|
||||
if (ptr != NULL && size == *ptr)
|
||||
{
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*ptr |= 1;
|
||||
|
||||
/* return aligned address of the block */
|
||||
return codec->allocptr2[scan];
|
||||
}
|
||||
}
|
||||
|
||||
/* alloc a new one and put it into the list */
|
||||
addr = (uint32_t *)malloc(size + sizeof(uint32_t) + LZMA_MIN_ALIGNMENT_BYTES);
|
||||
if (addr==NULL)
|
||||
return NULL;
|
||||
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
|
||||
{
|
||||
if (codec->allocptr[scan] == NULL)
|
||||
{
|
||||
/* store block address */
|
||||
codec->allocptr[scan] = addr;
|
||||
|
||||
/* compute aligned address, store it */
|
||||
vaddr = (uintptr_t)addr;
|
||||
vaddr = (vaddr + sizeof(uint32_t) + (LZMA_MIN_ALIGNMENT_BYTES-1)) & (~(LZMA_MIN_ALIGNMENT_BYTES-1));
|
||||
codec->allocptr2[scan] = (uint32_t*)vaddr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*addr = size | 1;
|
||||
|
||||
/* return aligned address */
|
||||
return (void*)vaddr;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
* lzma_fast_free - fast free for lzma, which
|
||||
* allocates and frees memory frequently
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
static void lzma_fast_free(void *p, void *address)
|
||||
{
|
||||
int scan;
|
||||
uint32_t *ptr = NULL;
|
||||
lzma_allocator *codec = NULL;
|
||||
|
||||
if (address == NULL)
|
||||
return;
|
||||
|
||||
codec = (lzma_allocator *)(p);
|
||||
|
||||
/* find the hunk */
|
||||
ptr = (uint32_t *)address;
|
||||
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
|
||||
{
|
||||
if (ptr == codec->allocptr2[scan])
|
||||
{
|
||||
/* clear the low bit of the size to allow matches */
|
||||
*codec->allocptr[scan] &= ~1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
* lzma_allocator_init
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void lzma_allocator_init(void* p)
|
||||
{
|
||||
lzma_allocator *codec = (lzma_allocator *)(p);
|
||||
|
||||
/* reset pointer list */
|
||||
memset(codec->allocptr, 0, sizeof(codec->allocptr));
|
||||
codec->Alloc = lzma_fast_alloc;
|
||||
codec->Free = lzma_fast_free;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
* lzma_allocator_free
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void lzma_allocator_free(void* p )
|
||||
{
|
||||
lzma_allocator *codec = (lzma_allocator *)(p);
|
||||
|
||||
/* free our memory */
|
||||
int i;
|
||||
for (i = 0 ; i < MAX_LZMA_ALLOCS ; i++)
|
||||
{
|
||||
if (codec->allocptr[i] != NULL)
|
||||
free(codec->allocptr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* LZMA DECOMPRESSOR
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------
|
||||
* lzma_codec_init - constructor
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
chd_error lzma_codec_init(void* codec, uint32_t hunkbytes)
|
||||
{
|
||||
CLzmaEncProps encoder_props;
|
||||
CLzmaEncHandle enc;
|
||||
uint8_t decoder_props[LZMA_PROPS_SIZE];
|
||||
lzma_allocator* alloc;
|
||||
size_t props_size;
|
||||
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
|
||||
|
||||
/* construct the decoder */
|
||||
LzmaDec_Construct(&lzma_codec->decoder);
|
||||
|
||||
/* FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK
|
||||
* This code assumes that the current version of the encoder imposes the same requirements on the
|
||||
* decoder as the encoder used to produce the file. This is not necessarily true. The format
|
||||
* needs to be changed so the encoder properties are written to the file.
|
||||
|
||||
* configure the properties like the compressor did */
|
||||
LzmaEncProps_Init(&encoder_props);
|
||||
encoder_props.level = 9;
|
||||
encoder_props.reduceSize = hunkbytes;
|
||||
LzmaEncProps_Normalize(&encoder_props);
|
||||
|
||||
/* convert to decoder properties */
|
||||
alloc = &lzma_codec->allocator;
|
||||
lzma_allocator_init(alloc);
|
||||
enc = LzmaEnc_Create((ISzAlloc*)alloc);
|
||||
if (!enc)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK)
|
||||
{
|
||||
LzmaEnc_Destroy(enc, (ISzAlloc*)&alloc, (ISzAlloc*)&alloc);
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
}
|
||||
props_size = sizeof(decoder_props);
|
||||
if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK)
|
||||
{
|
||||
LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc);
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
}
|
||||
LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc);
|
||||
|
||||
/* do memory allocations */
|
||||
if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
|
||||
/* Okay */
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
* lzma_codec_free
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void lzma_codec_free(void* codec)
|
||||
{
|
||||
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
|
||||
lzma_allocator* alloc = &lzma_codec->allocator;
|
||||
|
||||
/* free memory */
|
||||
LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator);
|
||||
lzma_allocator_free(alloc);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
* decompress - decompress data using the LZMA
|
||||
* codec
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
|
||||
{
|
||||
ELzmaStatus status;
|
||||
SRes res;
|
||||
size_t consumedlen, decodedlen;
|
||||
/* initialize */
|
||||
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
|
||||
LzmaDec_Init(&lzma_codec->decoder);
|
||||
|
||||
/* decode */
|
||||
consumedlen = complen;
|
||||
decodedlen = destlen;
|
||||
res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status);
|
||||
if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
/* cdlz */
|
||||
chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes)
|
||||
{
|
||||
chd_error ret;
|
||||
cdlz_codec_data* cdlz = (cdlz_codec_data*) codec;
|
||||
|
||||
/* allocate buffer */
|
||||
cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
|
||||
if (cdlz->buffer == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
|
||||
ret = lzma_codec_init(&cdlz->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
|
||||
if (ret != CHDERR_NONE)
|
||||
return ret;
|
||||
|
||||
#ifdef WANT_SUBCODE
|
||||
ret = zlib_codec_init(&cdlz->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
|
||||
if (ret != CHDERR_NONE)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
void cdlz_codec_free(void* codec)
|
||||
{
|
||||
cdlz_codec_data* cdlz = (cdlz_codec_data*) codec;
|
||||
|
||||
lzma_codec_free(&cdlz->base_decompressor);
|
||||
#ifdef WANT_SUBCODE
|
||||
zlib_codec_free(&cdlz->subcode_decompressor);
|
||||
#endif
|
||||
if (cdlz->buffer)
|
||||
free(cdlz->buffer);
|
||||
}
|
||||
|
||||
chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
|
||||
{
|
||||
#ifdef WANT_RAW_DATA_SECTOR
|
||||
uint8_t *sector;
|
||||
#endif
|
||||
uint32_t framenum;
|
||||
cdlz_codec_data* cdlz = (cdlz_codec_data*)codec;
|
||||
|
||||
/* determine header bytes */
|
||||
uint32_t frames = destlen / CD_FRAME_SIZE;
|
||||
uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;
|
||||
uint32_t ecc_bytes = (frames + 7) / 8;
|
||||
uint32_t header_bytes = ecc_bytes + complen_bytes;
|
||||
|
||||
/* extract compressed length of base */
|
||||
uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];
|
||||
if (complen_bytes > 2)
|
||||
complen_base = (complen_base << 8) | src[ecc_bytes + 2];
|
||||
|
||||
/* reset and decode */
|
||||
lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA);
|
||||
#ifdef WANT_SUBCODE
|
||||
if (header_bytes + complen_base >= complen)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
zlib_codec_decompress(&cdlz->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdlz->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
|
||||
#endif
|
||||
|
||||
/* reassemble the data */
|
||||
for (framenum = 0; framenum < frames; framenum++)
|
||||
{
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE], &cdlz->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
|
||||
#ifdef WANT_SUBCODE
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdlz->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);
|
||||
#endif
|
||||
|
||||
#ifdef WANT_RAW_DATA_SECTOR
|
||||
/* reconstitute the ECC data and sync header */
|
||||
sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE];
|
||||
if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)
|
||||
{
|
||||
memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header));
|
||||
ecc_generate(sector);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
313
src/minarch/libretro-common/formats/libchdr/libchdr_zlib.c
Normal file
313
src/minarch/libretro-common/formats/libchdr/libchdr_zlib.c
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/***************************************************************************
|
||||
|
||||
libchdr_zlib.c
|
||||
|
||||
MAME Compressed Hunks of Data file format
|
||||
|
||||
****************************************************************************
|
||||
|
||||
Copyright Aaron Giles
|
||||
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.
|
||||
* Neither the name 'MAME' nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY AARON GILES ''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 AARON GILES 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libchdr/chd.h>
|
||||
#include <libchdr/minmax.h>
|
||||
#include <libchdr/cdrom.h>
|
||||
#include <libchdr/huffman.h>
|
||||
#include <libchdr/libchdr_zlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
/* cdzl */
|
||||
|
||||
chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes)
|
||||
{
|
||||
chd_error ret;
|
||||
cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;
|
||||
|
||||
/* make sure the CHD's hunk size is an even multiple of the frame size */
|
||||
if (hunkbytes % CD_FRAME_SIZE != 0)
|
||||
return CHDERR_CODEC_ERROR;
|
||||
|
||||
cdzl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
|
||||
if (cdzl->buffer == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
|
||||
ret = zlib_codec_init(&cdzl->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
|
||||
if (ret != CHDERR_NONE)
|
||||
return ret;
|
||||
|
||||
#ifdef WANT_SUBCODE
|
||||
ret = zlib_codec_init(&cdzl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
|
||||
if (ret != CHDERR_NONE)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
void cdzl_codec_free(void *codec)
|
||||
{
|
||||
cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;
|
||||
|
||||
zlib_codec_free(&cdzl->base_decompressor);
|
||||
#ifdef WANT_SUBCODE
|
||||
zlib_codec_free(&cdzl->subcode_decompressor);
|
||||
#endif
|
||||
if (cdzl->buffer)
|
||||
free(cdzl->buffer);
|
||||
}
|
||||
|
||||
chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
|
||||
{
|
||||
#ifdef WANT_RAW_DATA_SECTOR
|
||||
uint8_t *sector;
|
||||
#endif
|
||||
uint32_t framenum;
|
||||
cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;
|
||||
|
||||
/* determine header bytes */
|
||||
uint32_t frames = destlen / CD_FRAME_SIZE;
|
||||
uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;
|
||||
uint32_t ecc_bytes = (frames + 7) / 8;
|
||||
uint32_t header_bytes = ecc_bytes + complen_bytes;
|
||||
|
||||
/* extract compressed length of base */
|
||||
uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];
|
||||
if (complen_bytes > 2)
|
||||
complen_base = (complen_base << 8) | src[ecc_bytes + 2];
|
||||
|
||||
/* reset and decode */
|
||||
zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA);
|
||||
#ifdef WANT_SUBCODE
|
||||
zlib_codec_decompress(&cdzl->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
|
||||
#endif
|
||||
|
||||
/* reassemble the data */
|
||||
for (framenum = 0; framenum < frames; framenum++)
|
||||
{
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
|
||||
#ifdef WANT_SUBCODE
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdzl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);
|
||||
#endif
|
||||
|
||||
#ifdef WANT_RAW_DATA_SECTOR
|
||||
/* reconstitute the ECC data and sync header */
|
||||
sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE];
|
||||
if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)
|
||||
{
|
||||
memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header));
|
||||
ecc_generate(sector);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
ZLIB COMPRESSION CODEC
|
||||
***************************************************************************/
|
||||
|
||||
/*-------------------------------------------------
|
||||
zlib_codec_init - initialize the ZLIB codec
|
||||
-------------------------------------------------*/
|
||||
|
||||
chd_error zlib_codec_init(void *codec, uint32_t hunkbytes)
|
||||
{
|
||||
int zerr;
|
||||
chd_error err;
|
||||
zlib_codec_data *data = (zlib_codec_data*)codec;
|
||||
|
||||
/* clear the buffers */
|
||||
memset(data, 0, sizeof(zlib_codec_data));
|
||||
|
||||
/* init the inflater first */
|
||||
data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */
|
||||
data->inflater.avail_in = 0;
|
||||
data->inflater.zalloc = zlib_fast_alloc;
|
||||
data->inflater.zfree = zlib_fast_free;
|
||||
data->inflater.opaque = &data->allocator;
|
||||
zerr = inflateInit2(&data->inflater, -MAX_WBITS);
|
||||
|
||||
/* convert errors */
|
||||
if (zerr == Z_MEM_ERROR)
|
||||
err = CHDERR_OUT_OF_MEMORY;
|
||||
else if (zerr != Z_OK)
|
||||
err = CHDERR_CODEC_ERROR;
|
||||
else
|
||||
err = CHDERR_NONE;
|
||||
|
||||
/* handle an error */
|
||||
if (err != CHDERR_NONE)
|
||||
free(data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
zlib_codec_free - free data for the ZLIB
|
||||
codec
|
||||
-------------------------------------------------*/
|
||||
|
||||
void zlib_codec_free(void *codec)
|
||||
{
|
||||
zlib_codec_data *data = (zlib_codec_data *)codec;
|
||||
|
||||
/* deinit the streams */
|
||||
if (data != NULL)
|
||||
{
|
||||
int i;
|
||||
zlib_allocator alloc;
|
||||
|
||||
inflateEnd(&data->inflater);
|
||||
|
||||
/* free our fast memory */
|
||||
alloc = data->allocator;
|
||||
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
|
||||
if (alloc.allocptr[i])
|
||||
free(alloc.allocptr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
zlib_codec_decompress - decomrpess data using
|
||||
the ZLIB codec
|
||||
-------------------------------------------------*/
|
||||
|
||||
chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
|
||||
{
|
||||
zlib_codec_data *data = (zlib_codec_data *)codec;
|
||||
int zerr;
|
||||
|
||||
/* reset the decompressor */
|
||||
data->inflater.next_in = (Bytef *)src;
|
||||
data->inflater.avail_in = complen;
|
||||
data->inflater.total_in = 0;
|
||||
data->inflater.next_out = (Bytef *)dest;
|
||||
data->inflater.avail_out = destlen;
|
||||
data->inflater.total_out = 0;
|
||||
zerr = inflateReset(&data->inflater);
|
||||
if (zerr != Z_OK)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
|
||||
/* do it */
|
||||
zerr = inflate(&data->inflater, Z_FINISH);
|
||||
(void)zerr;
|
||||
if (data->inflater.total_out != destlen)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
zlib_fast_alloc - fast malloc for ZLIB, which
|
||||
allocates and frees memory frequently
|
||||
-------------------------------------------------*/
|
||||
|
||||
/* Huge alignment values for possible SIMD optimization by compiler (NEON, SSE, AVX) */
|
||||
#define ZLIB_MIN_ALIGNMENT_BITS 512
|
||||
#define ZLIB_MIN_ALIGNMENT_BYTES (ZLIB_MIN_ALIGNMENT_BITS / 8)
|
||||
|
||||
voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size)
|
||||
{
|
||||
zlib_allocator *alloc = (zlib_allocator *)opaque;
|
||||
uintptr_t paddr = 0;
|
||||
UINT32 *ptr;
|
||||
int i;
|
||||
|
||||
/* compute the size, rounding to the nearest 1k */
|
||||
size = (size * items + 0x3ff) & ~0x3ff;
|
||||
|
||||
/* reuse a hunk if we can */
|
||||
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
|
||||
{
|
||||
ptr = alloc->allocptr[i];
|
||||
if (ptr && size == *ptr)
|
||||
{
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*ptr |= 1;
|
||||
|
||||
/* return aligned block address */
|
||||
return (voidpf)(alloc->allocptr2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* alloc a new one */
|
||||
ptr = (UINT32 *)malloc(size + sizeof(UINT32) + ZLIB_MIN_ALIGNMENT_BYTES);
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
/* put it into the list */
|
||||
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
|
||||
if (!alloc->allocptr[i])
|
||||
{
|
||||
alloc->allocptr[i] = ptr;
|
||||
paddr = (((uintptr_t)ptr) + sizeof(UINT32) + (ZLIB_MIN_ALIGNMENT_BYTES-1)) & (~(ZLIB_MIN_ALIGNMENT_BYTES-1));
|
||||
alloc->allocptr2[i] = (uint32_t*)paddr;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*ptr = size | 1;
|
||||
|
||||
/* return aligned block address */
|
||||
return (voidpf)paddr;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
zlib_fast_free - fast free for ZLIB, which
|
||||
allocates and frees memory frequently
|
||||
-------------------------------------------------*/
|
||||
|
||||
void zlib_fast_free(voidpf opaque, voidpf address)
|
||||
{
|
||||
zlib_allocator *alloc = (zlib_allocator *)opaque;
|
||||
UINT32 *ptr = (UINT32 *)address;
|
||||
int i;
|
||||
|
||||
/* find the hunk */
|
||||
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
|
||||
if (ptr == alloc->allocptr2[i])
|
||||
{
|
||||
/* clear the low bit of the size to allow matches */
|
||||
*(alloc->allocptr[i]) &= ~1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
457
src/minarch/libretro-common/formats/logiqx_dat/logiqx_dat.c
Normal file
457
src/minarch/libretro-common/formats/logiqx_dat/logiqx_dat.c
Normal file
|
|
@ -0,0 +1,457 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (logiqx_dat.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 <string/stdstring.h>
|
||||
#include <formats/rxml.h>
|
||||
|
||||
#include <formats/logiqx_dat.h>
|
||||
|
||||
/* Holds all internal DAT file data */
|
||||
struct logiqx_dat
|
||||
{
|
||||
rxml_document_t *data;
|
||||
rxml_node_t *current_node;
|
||||
};
|
||||
|
||||
/* List of HTML formatting codes that must
|
||||
* be replaced when parsing XML data */
|
||||
const char *logiqx_dat_html_code_list[][2] = {
|
||||
{"&", "&"},
|
||||
{"'", "'"},
|
||||
{">", ">"},
|
||||
{"<", "<"},
|
||||
{""", "\""}
|
||||
};
|
||||
|
||||
#define LOGIQX_DAT_HTML_CODE_LIST_SIZE 5
|
||||
|
||||
/* Validation */
|
||||
|
||||
/* Performs rudimentary validation of the specified
|
||||
* Logiqx XML DAT file path (not rigorous - just
|
||||
* enough to prevent obvious errors).
|
||||
* Also provides access to file size (DAT files can
|
||||
* be very large, so it is useful to have this information
|
||||
* on hand - i.e. so we can check that the system has
|
||||
* enough free memory to load the file). */
|
||||
bool logiqx_dat_path_is_valid(const char *path, uint64_t *file_size)
|
||||
{
|
||||
const char *file_ext = NULL;
|
||||
int32_t file_size_int;
|
||||
|
||||
if (string_is_empty(path))
|
||||
return false;
|
||||
|
||||
/* Check file extension */
|
||||
file_ext = path_get_extension(path);
|
||||
|
||||
if (string_is_empty(file_ext))
|
||||
return false;
|
||||
|
||||
if (!string_is_equal_noncase(file_ext, "dat") &&
|
||||
!string_is_equal_noncase(file_ext, "xml"))
|
||||
return false;
|
||||
|
||||
/* Ensure file exists */
|
||||
if (!path_is_valid(path))
|
||||
return false;
|
||||
|
||||
/* Get file size */
|
||||
file_size_int = path_get_size(path);
|
||||
|
||||
if (file_size_int <= 0)
|
||||
return false;
|
||||
|
||||
if (file_size)
|
||||
*file_size = (uint64_t)file_size_int;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* File initialisation/de-initialisation */
|
||||
|
||||
/* Loads specified Logiqx XML DAT file from disk.
|
||||
* Returned logiqx_dat_t object must be free'd using
|
||||
* logiqx_dat_free().
|
||||
* Returns NULL if file is invalid or a read error
|
||||
* occurs. */
|
||||
logiqx_dat_t *logiqx_dat_init(const char *path)
|
||||
{
|
||||
logiqx_dat_t *dat_file = NULL;
|
||||
rxml_node_t *root_node = NULL;
|
||||
|
||||
/* Check file path */
|
||||
if (!logiqx_dat_path_is_valid(path, NULL))
|
||||
goto error;
|
||||
|
||||
/* Create logiqx_dat_t object */
|
||||
dat_file = (logiqx_dat_t*)calloc(1, sizeof(*dat_file));
|
||||
|
||||
if (!dat_file)
|
||||
goto error;
|
||||
|
||||
/* Read file from disk */
|
||||
dat_file->data = rxml_load_document(path);
|
||||
|
||||
if (!dat_file->data)
|
||||
goto error;
|
||||
|
||||
/* Ensure root node has the correct name */
|
||||
root_node = rxml_root_node(dat_file->data);
|
||||
|
||||
if (!root_node)
|
||||
goto error;
|
||||
|
||||
if (string_is_empty(root_node->name))
|
||||
goto error;
|
||||
|
||||
/* > Logiqx XML uses: 'datafile'
|
||||
* > MAME List XML uses: 'mame'
|
||||
* > MAME 'Software List' uses: 'softwarelist' */
|
||||
if (!string_is_equal(root_node->name, "datafile") &&
|
||||
!string_is_equal(root_node->name, "mame") &&
|
||||
!string_is_equal(root_node->name, "softwarelist"))
|
||||
goto error;
|
||||
|
||||
/* Get pointer to initial child node */
|
||||
dat_file->current_node = root_node->children;
|
||||
|
||||
if (!dat_file->current_node)
|
||||
goto error;
|
||||
|
||||
/* All is well - return logiqx_dat_t object */
|
||||
return dat_file;
|
||||
|
||||
error:
|
||||
logiqx_dat_free(dat_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Frees specified DAT file */
|
||||
void logiqx_dat_free(logiqx_dat_t *dat_file)
|
||||
{
|
||||
if (!dat_file)
|
||||
return;
|
||||
|
||||
dat_file->current_node = NULL;
|
||||
|
||||
if (dat_file->data)
|
||||
{
|
||||
rxml_free_document(dat_file->data);
|
||||
dat_file->data = NULL;
|
||||
}
|
||||
|
||||
free(dat_file);
|
||||
dat_file = NULL;
|
||||
}
|
||||
|
||||
/* Game information access */
|
||||
|
||||
/* Returns true if specified node is a 'game' entry */
|
||||
static bool logiqx_dat_is_game_node(rxml_node_t *node)
|
||||
{
|
||||
const char *node_name = NULL;
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
/* Check node name */
|
||||
node_name = node->name;
|
||||
|
||||
if (string_is_empty(node_name))
|
||||
return false;
|
||||
|
||||
/* > Logiqx XML uses: 'game'
|
||||
* > MAME List XML uses: 'machine'
|
||||
* > MAME 'Software List' uses: 'software' */
|
||||
return string_is_equal(node_name, "game") ||
|
||||
string_is_equal(node_name, "machine") ||
|
||||
string_is_equal(node_name, "software");
|
||||
}
|
||||
|
||||
/* Returns true if specified node is a game
|
||||
* node containing information for a game with
|
||||
* the specified name */
|
||||
static bool logiqx_dat_game_node_matches_name(
|
||||
rxml_node_t *node, const char *game_name)
|
||||
{
|
||||
const char *node_game_name = NULL;
|
||||
|
||||
if (!logiqx_dat_is_game_node(node) ||
|
||||
string_is_empty(game_name))
|
||||
return false;
|
||||
|
||||
/* Get 'name' attribute of XML node */
|
||||
node_game_name = rxml_node_attrib(node, "name");
|
||||
|
||||
if (string_is_empty(node_game_name))
|
||||
return false;
|
||||
|
||||
return string_is_equal(node_game_name, game_name);
|
||||
}
|
||||
|
||||
/* The XML element data strings returned from
|
||||
* DAT files are very 'messy'. This function
|
||||
* removes all cruft, replaces formatting strings
|
||||
* and copies the result (if valid) to 'str' */
|
||||
static void logiqx_dat_sanitise_element_data(
|
||||
const char *data, char *str, size_t len)
|
||||
{
|
||||
char sanitised_data[PATH_MAX_LENGTH];
|
||||
size_t i;
|
||||
|
||||
sanitised_data[0] = '\0';
|
||||
|
||||
if (string_is_empty(data))
|
||||
return;
|
||||
|
||||
strlcpy(sanitised_data, data, sizeof(sanitised_data));
|
||||
|
||||
/* Element data includes leading/trailing
|
||||
* newline characters - trim them away */
|
||||
string_trim_whitespace(sanitised_data);
|
||||
|
||||
if (string_is_empty(sanitised_data))
|
||||
return;
|
||||
|
||||
/* XML has a number of special characters that
|
||||
* are handled using a HTML formatting codes.
|
||||
* All of these have to be replaced...
|
||||
* & -> &
|
||||
* ' -> '
|
||||
* > -> >
|
||||
* < -> <
|
||||
* " -> "
|
||||
*/
|
||||
for (i = 0; i < LOGIQX_DAT_HTML_CODE_LIST_SIZE; i++)
|
||||
{
|
||||
const char *find_string = logiqx_dat_html_code_list[i][0];
|
||||
const char *replace_string = logiqx_dat_html_code_list[i][1];
|
||||
|
||||
/* string_replace_substring() is expensive
|
||||
* > only invoke if element string contains
|
||||
* HTML code */
|
||||
if (strstr(sanitised_data, find_string))
|
||||
{
|
||||
char *tmp = string_replace_substring(
|
||||
sanitised_data,
|
||||
find_string, strlen(find_string),
|
||||
replace_string, strlen(replace_string));
|
||||
|
||||
if (!string_is_empty(tmp))
|
||||
strlcpy(sanitised_data, tmp, sizeof(sanitised_data));
|
||||
|
||||
if (tmp)
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (string_is_empty(sanitised_data))
|
||||
return;
|
||||
|
||||
/* All is well - can copy result */
|
||||
strlcpy(str, sanitised_data, len);
|
||||
}
|
||||
|
||||
/* Extracts game information from specified node.
|
||||
* Returns false if node is invalid */
|
||||
static bool logiqx_dat_parse_game_node(
|
||||
rxml_node_t *node, logiqx_dat_game_info_t *game_info)
|
||||
{
|
||||
const char *game_name = NULL;
|
||||
const char *is_bios = NULL;
|
||||
const char *is_runnable = NULL;
|
||||
rxml_node_t *info_node = NULL;
|
||||
bool description_found = false;
|
||||
bool year_found = false;
|
||||
bool manufacturer_found = false;
|
||||
|
||||
if (!logiqx_dat_is_game_node(node))
|
||||
return false;
|
||||
|
||||
if (!game_info)
|
||||
return false;
|
||||
|
||||
/* Initialise logiqx_dat_game_info_t object */
|
||||
game_info->name[0] = '\0';
|
||||
game_info->description[0] = '\0';
|
||||
game_info->year[0] = '\0';
|
||||
game_info->manufacturer[0] = '\0';
|
||||
game_info->is_bios = false;
|
||||
game_info->is_runnable = true;
|
||||
|
||||
/* Get game name */
|
||||
game_name = rxml_node_attrib(node, "name");
|
||||
|
||||
if (!string_is_empty(game_name))
|
||||
strlcpy(game_info->name, game_name, sizeof(game_info->name));
|
||||
|
||||
/* Get 'is bios' status */
|
||||
is_bios = rxml_node_attrib(node, "isbios");
|
||||
|
||||
if (!string_is_empty(is_bios))
|
||||
game_info->is_bios = string_is_equal(is_bios, "yes");
|
||||
|
||||
/* Get 'is runnable' status
|
||||
* > Note: This attribute only exists in MAME List
|
||||
* XML files, but there is no harm in checking for
|
||||
* it generally. For normal Logiqx XML files,
|
||||
* 'is runnable' is just the inverse of 'is bios' */
|
||||
is_runnable = rxml_node_attrib(node, "runnable");
|
||||
|
||||
if (!string_is_empty(is_runnable))
|
||||
game_info->is_runnable = string_is_equal(is_runnable, "yes");
|
||||
else
|
||||
game_info->is_runnable = !game_info->is_bios;
|
||||
|
||||
/* Loop over all game info nodes */
|
||||
for (info_node = node->children; info_node; info_node = info_node->next)
|
||||
{
|
||||
const char *info_node_name = info_node->name;
|
||||
const char *info_node_data = info_node->data;
|
||||
|
||||
if (string_is_empty(info_node_name))
|
||||
continue;
|
||||
|
||||
/* Check description */
|
||||
if (string_is_equal(info_node_name, "description"))
|
||||
{
|
||||
logiqx_dat_sanitise_element_data(
|
||||
info_node_data, game_info->description,
|
||||
sizeof(game_info->description));
|
||||
description_found = true;
|
||||
}
|
||||
/* Check year */
|
||||
else if (string_is_equal(info_node_name, "year"))
|
||||
{
|
||||
logiqx_dat_sanitise_element_data(
|
||||
info_node_data, game_info->year,
|
||||
sizeof(game_info->year));
|
||||
year_found = true;
|
||||
}
|
||||
/* Check manufacturer */
|
||||
else if (string_is_equal(info_node_name, "manufacturer"))
|
||||
{
|
||||
logiqx_dat_sanitise_element_data(
|
||||
info_node_data, game_info->manufacturer,
|
||||
sizeof(game_info->manufacturer));
|
||||
manufacturer_found = true;
|
||||
}
|
||||
|
||||
/* If all required entries have been found,
|
||||
* can end loop */
|
||||
if (description_found && year_found && manufacturer_found)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Sets/resets internal node pointer to the first
|
||||
* entry in the DAT file */
|
||||
void logiqx_dat_set_first(logiqx_dat_t *dat_file)
|
||||
{
|
||||
rxml_node_t *root_node = NULL;
|
||||
|
||||
if (!dat_file)
|
||||
return;
|
||||
|
||||
if (!dat_file->data)
|
||||
return;
|
||||
|
||||
/* Get root node */
|
||||
root_node = rxml_root_node(dat_file->data);
|
||||
|
||||
if (!root_node)
|
||||
{
|
||||
dat_file->current_node = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get pointer to initial child node */
|
||||
dat_file->current_node = root_node->children;
|
||||
}
|
||||
|
||||
/* Fetches game information for the current entry
|
||||
* in the DAT file and increments the internal node
|
||||
* pointer.
|
||||
* Returns false if the end of the DAT file has been
|
||||
* reached (in which case 'game_info' will be invalid) */
|
||||
bool logiqx_dat_get_next(
|
||||
logiqx_dat_t *dat_file, logiqx_dat_game_info_t *game_info)
|
||||
{
|
||||
if (!dat_file || !game_info)
|
||||
return false;
|
||||
|
||||
if (!dat_file->data)
|
||||
return false;
|
||||
|
||||
while (dat_file->current_node)
|
||||
{
|
||||
rxml_node_t *current_node = dat_file->current_node;
|
||||
|
||||
/* Whatever happens, internal node pointer must
|
||||
* be 'incremented' */
|
||||
dat_file->current_node = dat_file->current_node->next;
|
||||
|
||||
/* If this is a game node, extract info
|
||||
* and return */
|
||||
if (logiqx_dat_is_game_node(current_node))
|
||||
return logiqx_dat_parse_game_node(current_node, game_info);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fetches information for the specified game.
|
||||
* Returns false if game does not exist, or arguments
|
||||
* are invalid. */
|
||||
bool logiqx_dat_search(
|
||||
logiqx_dat_t *dat_file, const char *game_name,
|
||||
logiqx_dat_game_info_t *game_info)
|
||||
{
|
||||
rxml_node_t *root_node = NULL;
|
||||
rxml_node_t *game_node = NULL;
|
||||
|
||||
if (!dat_file || !game_info || string_is_empty(game_name))
|
||||
return false;
|
||||
|
||||
if (!dat_file->data)
|
||||
return false;
|
||||
|
||||
/* Get root node */
|
||||
root_node = rxml_root_node(dat_file->data);
|
||||
|
||||
if (!root_node)
|
||||
return false;
|
||||
|
||||
/* Loop over all child nodes of the DAT file */
|
||||
for (game_node = root_node->children; game_node; game_node = game_node->next)
|
||||
{
|
||||
/* If this is the requested game, fetch info and return */
|
||||
if (logiqx_dat_game_node_matches_name(game_node, game_name))
|
||||
return logiqx_dat_parse_game_node(game_node, game_info);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
633
src/minarch/libretro-common/formats/m3u/m3u_file.c
Normal file
633
src/minarch/libretro-common/formats/m3u/m3u_file.c
Normal file
|
|
@ -0,0 +1,633 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (m3u_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 <retro_miscellaneous.h>
|
||||
|
||||
#include <string/stdstring.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <file/file_path.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <array/rbuf.h>
|
||||
|
||||
#include <formats/m3u_file.h>
|
||||
|
||||
/* We parse the following types of entry label:
|
||||
* - '#LABEL:<label>' non-standard, but used by
|
||||
* some cores
|
||||
* - '#EXTINF:<runtime>,<label>' standard extended
|
||||
* M3U directive
|
||||
* - '<content path>|<label>' non-standard, but
|
||||
* used by some cores
|
||||
* All other comments/directives are ignored */
|
||||
#define M3U_FILE_COMMENT '#'
|
||||
#define M3U_FILE_NONSTD_LABEL "#LABEL:"
|
||||
#define M3U_FILE_EXTSTD_LABEL "#EXTINF:"
|
||||
#define M3U_FILE_EXTSTD_LABEL_TOKEN ','
|
||||
#define M3U_FILE_RETRO_LABEL_TOKEN '|'
|
||||
|
||||
/* Holds all internal M3U file data
|
||||
* > Note the awkward name: 'content_m3u_file'
|
||||
* If we used just 'm3u_file' here, it would
|
||||
* lead to conflicts elsewhere... */
|
||||
struct content_m3u_file
|
||||
{
|
||||
char *path;
|
||||
m3u_file_entry_t *entries;
|
||||
};
|
||||
|
||||
/* File Initialisation / De-Initialisation */
|
||||
|
||||
/* Reads M3U file contents from disk
|
||||
* - Does nothing if file does not exist
|
||||
* - Returns false in the event of an error */
|
||||
static bool m3u_file_load(m3u_file_t *m3u_file)
|
||||
{
|
||||
const char *file_ext = NULL;
|
||||
int64_t file_len = 0;
|
||||
uint8_t *file_buf = NULL;
|
||||
struct string_list *lines = NULL;
|
||||
bool success = false;
|
||||
size_t i;
|
||||
char entry_path[PATH_MAX_LENGTH];
|
||||
char entry_label[PATH_MAX_LENGTH];
|
||||
|
||||
entry_path[0] = '\0';
|
||||
entry_label[0] = '\0';
|
||||
|
||||
if (!m3u_file)
|
||||
goto end;
|
||||
|
||||
/* Check whether file exists
|
||||
* > If path is empty, then an error
|
||||
* has occurred... */
|
||||
if (string_is_empty(m3u_file->path))
|
||||
goto end;
|
||||
|
||||
/* > File must have the correct extension */
|
||||
file_ext = path_get_extension(m3u_file->path);
|
||||
|
||||
if (string_is_empty(file_ext) ||
|
||||
!string_is_equal_noncase(file_ext, M3U_FILE_EXT))
|
||||
goto end;
|
||||
|
||||
/* > If file does not exist, no action
|
||||
* is required */
|
||||
if (!path_is_valid(m3u_file->path))
|
||||
{
|
||||
success = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Read file from disk */
|
||||
if (filestream_read_file(m3u_file->path, (void**)&file_buf, &file_len) >= 0)
|
||||
{
|
||||
/* Split file into lines */
|
||||
if (file_len > 0)
|
||||
lines = string_split((const char*)file_buf, "\n");
|
||||
|
||||
/* File buffer no longer required */
|
||||
if (file_buf)
|
||||
{
|
||||
free(file_buf);
|
||||
file_buf = NULL;
|
||||
}
|
||||
}
|
||||
/* File IO error... */
|
||||
else
|
||||
goto end;
|
||||
|
||||
/* If file was empty, no action is required */
|
||||
if (!lines)
|
||||
{
|
||||
success = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Parse lines of file */
|
||||
for (i = 0; i < lines->size; i++)
|
||||
{
|
||||
const char *line = lines->elems[i].data;
|
||||
|
||||
if (string_is_empty(line))
|
||||
continue;
|
||||
|
||||
/* Determine line 'type' */
|
||||
|
||||
/* > '#LABEL:' */
|
||||
if (string_starts_with_size(line, M3U_FILE_NONSTD_LABEL,
|
||||
STRLEN_CONST(M3U_FILE_NONSTD_LABEL)))
|
||||
{
|
||||
/* Label is the string to the right
|
||||
* of '#LABEL:' */
|
||||
const char *label = line + STRLEN_CONST(M3U_FILE_NONSTD_LABEL);
|
||||
|
||||
if (!string_is_empty(label))
|
||||
{
|
||||
strlcpy(
|
||||
entry_label, line + STRLEN_CONST(M3U_FILE_NONSTD_LABEL),
|
||||
sizeof(entry_label));
|
||||
string_trim_whitespace(entry_label);
|
||||
}
|
||||
}
|
||||
/* > '#EXTINF:' */
|
||||
else if (string_starts_with_size(line, M3U_FILE_EXTSTD_LABEL,
|
||||
STRLEN_CONST(M3U_FILE_EXTSTD_LABEL)))
|
||||
{
|
||||
/* Label is the string to the right
|
||||
* of the first comma */
|
||||
const char* label_ptr = strchr(
|
||||
line + STRLEN_CONST(M3U_FILE_EXTSTD_LABEL),
|
||||
M3U_FILE_EXTSTD_LABEL_TOKEN);
|
||||
|
||||
if (!string_is_empty(label_ptr))
|
||||
{
|
||||
label_ptr++;
|
||||
if (!string_is_empty(label_ptr))
|
||||
{
|
||||
strlcpy(entry_label, label_ptr, sizeof(entry_label));
|
||||
string_trim_whitespace(entry_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* > Ignore other comments/directives */
|
||||
else if (line[0] == M3U_FILE_COMMENT)
|
||||
continue;
|
||||
/* > An actual 'content' line */
|
||||
else
|
||||
{
|
||||
/* This is normally a file name/path, but may
|
||||
* have the format <content path>|<label> */
|
||||
const char *token_ptr = strchr(line, M3U_FILE_RETRO_LABEL_TOKEN);
|
||||
|
||||
if (token_ptr)
|
||||
{
|
||||
size_t len = (size_t)(1 + token_ptr - line);
|
||||
|
||||
/* Get entry_path segment */
|
||||
if (len > 0)
|
||||
{
|
||||
memset(entry_path, 0, sizeof(entry_path));
|
||||
strlcpy(
|
||||
entry_path, line,
|
||||
((len < PATH_MAX_LENGTH ?
|
||||
len : PATH_MAX_LENGTH) * sizeof(char)));
|
||||
string_trim_whitespace(entry_path);
|
||||
}
|
||||
|
||||
/* Get entry_label segment */
|
||||
token_ptr++;
|
||||
if (*token_ptr != '\0')
|
||||
{
|
||||
strlcpy(entry_label, token_ptr, sizeof(entry_label));
|
||||
string_trim_whitespace(entry_label);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just a normal file name/path */
|
||||
strlcpy(entry_path, line, sizeof(entry_path));
|
||||
string_trim_whitespace(entry_path);
|
||||
}
|
||||
|
||||
/* Add entry to file
|
||||
* > Note: The only way that m3u_file_add_entry()
|
||||
* can fail here is if we run out of memory.
|
||||
* This is a critical error, and m3u_file must
|
||||
* be considered invalid in this case */
|
||||
if (!string_is_empty(entry_path) &&
|
||||
!m3u_file_add_entry(m3u_file, entry_path, entry_label))
|
||||
goto end;
|
||||
|
||||
/* Reset entry_path/entry_label */
|
||||
entry_path[0] = '\0';
|
||||
entry_label[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
end:
|
||||
/* Clean up */
|
||||
if (lines)
|
||||
{
|
||||
string_list_free(lines);
|
||||
lines = NULL;
|
||||
}
|
||||
|
||||
if (file_buf)
|
||||
{
|
||||
free(file_buf);
|
||||
file_buf = NULL;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Creates and initialises an M3U file
|
||||
* - If 'path' refers to an existing file,
|
||||
* contents is parsed
|
||||
* - If path does not exist, an empty M3U file
|
||||
* is created
|
||||
* - Returned m3u_file_t object must be free'd using
|
||||
* m3u_file_free()
|
||||
* - Returns NULL in the event of an error */
|
||||
m3u_file_t *m3u_file_init(const char *path)
|
||||
{
|
||||
m3u_file_t *m3u_file = NULL;
|
||||
char m3u_path[PATH_MAX_LENGTH];
|
||||
|
||||
m3u_path[0] = '\0';
|
||||
|
||||
/* Sanity check */
|
||||
if (string_is_empty(path))
|
||||
return NULL;
|
||||
|
||||
/* Get 'real' file path */
|
||||
strlcpy(m3u_path, path, sizeof(m3u_path));
|
||||
path_resolve_realpath(m3u_path, sizeof(m3u_path), false);
|
||||
|
||||
if (string_is_empty(m3u_path))
|
||||
return NULL;
|
||||
|
||||
/* Create m3u_file_t object */
|
||||
m3u_file = (m3u_file_t*)malloc(sizeof(*m3u_file));
|
||||
|
||||
if (!m3u_file)
|
||||
return NULL;
|
||||
|
||||
/* Initialise members */
|
||||
m3u_file->path = NULL;
|
||||
m3u_file->entries = NULL;
|
||||
|
||||
/* Copy file path */
|
||||
m3u_file->path = strdup(m3u_path);
|
||||
|
||||
/* Read existing file contents from
|
||||
* disk, if required */
|
||||
if (!m3u_file_load(m3u_file))
|
||||
{
|
||||
m3u_file_free(m3u_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m3u_file;
|
||||
}
|
||||
|
||||
/* Frees specified M3U file entry */
|
||||
static void m3u_file_free_entry(m3u_file_entry_t *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
if (entry->path)
|
||||
free(entry->path);
|
||||
|
||||
if (entry->full_path)
|
||||
free(entry->full_path);
|
||||
|
||||
if (entry->label)
|
||||
free(entry->label);
|
||||
|
||||
entry->path = NULL;
|
||||
entry->full_path = NULL;
|
||||
entry->label = NULL;
|
||||
}
|
||||
|
||||
/* Frees specified M3U file */
|
||||
void m3u_file_free(m3u_file_t *m3u_file)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!m3u_file)
|
||||
return;
|
||||
|
||||
if (m3u_file->path)
|
||||
free(m3u_file->path);
|
||||
|
||||
m3u_file->path = NULL;
|
||||
|
||||
/* Free entries */
|
||||
if (m3u_file->entries)
|
||||
{
|
||||
for (i = 0; i < RBUF_LEN(m3u_file->entries); i++)
|
||||
{
|
||||
m3u_file_entry_t *entry = &m3u_file->entries[i];
|
||||
m3u_file_free_entry(entry);
|
||||
}
|
||||
|
||||
RBUF_FREE(m3u_file->entries);
|
||||
}
|
||||
|
||||
free(m3u_file);
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
|
||||
/* Returns M3U file path */
|
||||
char *m3u_file_get_path(m3u_file_t *m3u_file)
|
||||
{
|
||||
if (!m3u_file)
|
||||
return NULL;
|
||||
|
||||
return m3u_file->path;
|
||||
}
|
||||
|
||||
/* Returns number of entries in M3U file */
|
||||
size_t m3u_file_get_size(m3u_file_t *m3u_file)
|
||||
{
|
||||
if (!m3u_file)
|
||||
return 0;
|
||||
|
||||
return RBUF_LEN(m3u_file->entries);
|
||||
}
|
||||
|
||||
/* Fetches specified M3U file entry
|
||||
* - Returns false if 'idx' is invalid, or internal
|
||||
* entry is NULL */
|
||||
bool m3u_file_get_entry(
|
||||
m3u_file_t *m3u_file, size_t idx, m3u_file_entry_t **entry)
|
||||
{
|
||||
if (!m3u_file ||
|
||||
!entry ||
|
||||
(idx >= RBUF_LEN(m3u_file->entries)))
|
||||
return false;
|
||||
|
||||
*entry = &m3u_file->entries[idx];
|
||||
|
||||
if (!*entry)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Setters */
|
||||
|
||||
/* Adds specified entry to the M3U file
|
||||
* - Returns false if path is invalid, or
|
||||
* memory could not be allocated for the
|
||||
* entry */
|
||||
bool m3u_file_add_entry(
|
||||
m3u_file_t *m3u_file, const char *path, const char *label)
|
||||
{
|
||||
m3u_file_entry_t *entry = NULL;
|
||||
size_t num_entries;
|
||||
char full_path[PATH_MAX_LENGTH];
|
||||
|
||||
full_path[0] = '\0';
|
||||
|
||||
if (!m3u_file || string_is_empty(path))
|
||||
return false;
|
||||
|
||||
/* Get current number of file entries */
|
||||
num_entries = RBUF_LEN(m3u_file->entries);
|
||||
|
||||
/* Attempt to allocate memory for new entry */
|
||||
if (!RBUF_TRYFIT(m3u_file->entries, num_entries + 1))
|
||||
return false;
|
||||
|
||||
/* Allocation successful - increment array size */
|
||||
RBUF_RESIZE(m3u_file->entries, num_entries + 1);
|
||||
|
||||
/* Fetch entry at end of list, and zero-initialise
|
||||
* members */
|
||||
entry = &m3u_file->entries[num_entries];
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
/* Copy path and label */
|
||||
entry->path = strdup(path);
|
||||
|
||||
if (!string_is_empty(label))
|
||||
entry->label = strdup(label);
|
||||
|
||||
/* Populate 'full_path' field */
|
||||
if (path_is_absolute(path))
|
||||
{
|
||||
strlcpy(full_path, path, sizeof(full_path));
|
||||
path_resolve_realpath(full_path, sizeof(full_path), false);
|
||||
}
|
||||
else
|
||||
fill_pathname_resolve_relative(
|
||||
full_path, m3u_file->path, path,
|
||||
sizeof(full_path));
|
||||
|
||||
/* Handle unforeseen errors... */
|
||||
if (string_is_empty(full_path))
|
||||
{
|
||||
m3u_file_free_entry(entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
entry->full_path = strdup(full_path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Removes all entries in M3U file */
|
||||
void m3u_file_clear(m3u_file_t *m3u_file)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!m3u_file)
|
||||
return;
|
||||
|
||||
if (m3u_file->entries)
|
||||
{
|
||||
for (i = 0; i < RBUF_LEN(m3u_file->entries); i++)
|
||||
{
|
||||
m3u_file_entry_t *entry = &m3u_file->entries[i];
|
||||
m3u_file_free_entry(entry);
|
||||
}
|
||||
|
||||
RBUF_FREE(m3u_file->entries);
|
||||
}
|
||||
}
|
||||
|
||||
/* Saving */
|
||||
|
||||
/* Saves M3U file to disk
|
||||
* - Setting 'label_type' to M3U_FILE_LABEL_NONE
|
||||
* just outputs entry paths - this the most
|
||||
* common format supported by most cores
|
||||
* - Returns false in the event of an error */
|
||||
bool m3u_file_save(
|
||||
m3u_file_t *m3u_file, enum m3u_file_label_type label_type)
|
||||
{
|
||||
RFILE *file = NULL;
|
||||
size_t i;
|
||||
char base_dir[PATH_MAX_LENGTH];
|
||||
|
||||
base_dir[0] = '\0';
|
||||
|
||||
if (!m3u_file || !m3u_file->entries)
|
||||
return false;
|
||||
|
||||
/* This should never happen */
|
||||
if (string_is_empty(m3u_file->path))
|
||||
return false;
|
||||
|
||||
/* Get M3U file base directory */
|
||||
if (find_last_slash(m3u_file->path))
|
||||
{
|
||||
strlcpy(base_dir, m3u_file->path, sizeof(base_dir));
|
||||
path_basedir(base_dir);
|
||||
}
|
||||
|
||||
/* Open file for writing */
|
||||
file = filestream_open(
|
||||
m3u_file->path,
|
||||
RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
/* Loop over entries */
|
||||
for (i = 0; i < RBUF_LEN(m3u_file->entries); i++)
|
||||
{
|
||||
m3u_file_entry_t *entry = &m3u_file->entries[i];
|
||||
char entry_path[PATH_MAX_LENGTH];
|
||||
|
||||
entry_path[0] = '\0';
|
||||
|
||||
if (!entry || string_is_empty(entry->full_path))
|
||||
continue;
|
||||
|
||||
/* When writing M3U files, entry paths are
|
||||
* always relative */
|
||||
if (string_is_empty(base_dir))
|
||||
strlcpy(
|
||||
entry_path, entry->full_path,
|
||||
sizeof(entry_path));
|
||||
else
|
||||
path_relative_to(
|
||||
entry_path, entry->full_path, base_dir,
|
||||
sizeof(entry_path));
|
||||
|
||||
if (string_is_empty(entry_path))
|
||||
continue;
|
||||
|
||||
/* Check if we need to write a label */
|
||||
if (!string_is_empty(entry->label))
|
||||
{
|
||||
switch (label_type)
|
||||
{
|
||||
case M3U_FILE_LABEL_NONSTD:
|
||||
filestream_printf(
|
||||
file, "%s%s\n%s\n",
|
||||
M3U_FILE_NONSTD_LABEL, entry->label,
|
||||
entry_path);
|
||||
break;
|
||||
case M3U_FILE_LABEL_EXTSTD:
|
||||
filestream_printf(
|
||||
file, "%s%c%s\n%s\n",
|
||||
M3U_FILE_EXTSTD_LABEL, M3U_FILE_EXTSTD_LABEL_TOKEN, entry->label,
|
||||
entry_path);
|
||||
break;
|
||||
case M3U_FILE_LABEL_RETRO:
|
||||
filestream_printf(
|
||||
file, "%s%c%s\n",
|
||||
entry_path, M3U_FILE_RETRO_LABEL_TOKEN, entry->label);
|
||||
break;
|
||||
case M3U_FILE_LABEL_NONE:
|
||||
default:
|
||||
filestream_printf(
|
||||
file, "%s\n", entry_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* No label - just write entry path */
|
||||
else
|
||||
filestream_printf(
|
||||
file, "%s\n", entry_path);
|
||||
}
|
||||
|
||||
/* Close file */
|
||||
filestream_close(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
/* Internal qsort function */
|
||||
static int m3u_file_qsort_func(
|
||||
const m3u_file_entry_t *a, const m3u_file_entry_t *b)
|
||||
{
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
|
||||
if (string_is_empty(a->full_path) || string_is_empty(b->full_path))
|
||||
return 0;
|
||||
|
||||
return strcasecmp(a->full_path, b->full_path);
|
||||
}
|
||||
|
||||
/* Sorts M3U file entries in alphabetical order */
|
||||
void m3u_file_qsort(m3u_file_t *m3u_file)
|
||||
{
|
||||
size_t num_entries;
|
||||
|
||||
if (!m3u_file)
|
||||
return;
|
||||
|
||||
num_entries = RBUF_LEN(m3u_file->entries);
|
||||
|
||||
if (num_entries < 2)
|
||||
return;
|
||||
|
||||
qsort(
|
||||
m3u_file->entries, num_entries,
|
||||
sizeof(m3u_file_entry_t),
|
||||
(int (*)(const void *, const void *))m3u_file_qsort_func);
|
||||
}
|
||||
|
||||
/* Returns true if specified path corresponds
|
||||
* to an M3U file (simple convenience function) */
|
||||
bool m3u_file_is_m3u(const char *path)
|
||||
{
|
||||
const char *file_ext = NULL;
|
||||
int32_t file_size;
|
||||
|
||||
if (string_is_empty(path))
|
||||
return false;
|
||||
|
||||
/* Check file extension */
|
||||
file_ext = path_get_extension(path);
|
||||
|
||||
if (string_is_empty(file_ext))
|
||||
return false;
|
||||
|
||||
if (!string_is_equal_noncase(file_ext, M3U_FILE_EXT))
|
||||
return false;
|
||||
|
||||
/* Ensure file exists */
|
||||
if (!path_is_valid(path))
|
||||
return false;
|
||||
|
||||
/* Ensure we have non-zero file size */
|
||||
file_size = path_get_size(path);
|
||||
|
||||
if (file_size <= 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
1255
src/minarch/libretro-common/formats/png/rpng.c
Normal file
1255
src/minarch/libretro-common/formats/png/rpng.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
Loading…
Add table
Add a link
Reference in a new issue