Use libretro-common
Signed-off-by: Mahyar Koshkouei <mahyar.koshkouei@gmail.com>
This commit is contained in:
		
							parent
							
								
									a8f84fa9b4
								
							
						
					
					
						commit
						dde40b7732
					
				
							
								
								
									
										2
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										2
									
								
								Makefile
								
								
								
								
							|  | @ -113,7 +113,7 @@ endif | |||
| 
 | ||||
| OBJECTS	:= mpv-libretro.o | ||||
| LDFLAGS	+= -lmpv -lm | ||||
| CFLAGS	+= -Wall -pedantic -std=c99 -I. | ||||
| CFLAGS	+= -Wall -pedantic -std=c99 -I./libretro-common/include/ | ||||
| 
 | ||||
| ifneq (,$(findstring gles,$(platform))) | ||||
|    LDFLAGS += -ldl  | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| *.o | ||||
|  | @ -0,0 +1,318 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (audio_mix.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <audio/audio_mix.h> | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
| #include <emmintrin.h> | ||||
| #elif defined(__ALTIVEC__) | ||||
| #include <altivec.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <memalign.h> | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <audio/audio_mix.h> | ||||
| #include <streams/file_stream.h> | ||||
| #include <audio/conversion/float_to_s16.h> | ||||
| #include <audio/conversion/s16_to_float.h> | ||||
| 
 | ||||
| void audio_mix_volume_C(float *out, const float *in, float vol, size_t samples) | ||||
| { | ||||
|    size_t i; | ||||
|    for (i = 0; i < samples; i++) | ||||
|       out[i] += in[i] * vol; | ||||
| } | ||||
| 
 | ||||
| #ifdef __SSE2__ | ||||
| void audio_mix_volume_SSE2(float *out, const float *in, float vol, size_t samples) | ||||
| { | ||||
|    size_t i; | ||||
|    __m128 volume = _mm_set1_ps(vol); | ||||
| 
 | ||||
|    for (i = 0; i + 16 <= samples; i += 16, out += 16, in += 16) | ||||
|    { | ||||
|       unsigned j; | ||||
|       __m128 input[4]; | ||||
|       __m128 additive[4]; | ||||
| 
 | ||||
|       input[0]    = _mm_loadu_ps(out +  0); | ||||
|       input[1]    = _mm_loadu_ps(out +  4); | ||||
|       input[2]    = _mm_loadu_ps(out +  8); | ||||
|       input[3]    = _mm_loadu_ps(out + 12); | ||||
| 
 | ||||
|       additive[0] = _mm_mul_ps(volume, _mm_loadu_ps(in +  0)); | ||||
|       additive[1] = _mm_mul_ps(volume, _mm_loadu_ps(in +  4)); | ||||
|       additive[2] = _mm_mul_ps(volume, _mm_loadu_ps(in +  8)); | ||||
|       additive[3] = _mm_mul_ps(volume, _mm_loadu_ps(in + 12)); | ||||
| 
 | ||||
|       for (j = 0; j < 4; j++) | ||||
|          _mm_storeu_ps(out + 4 * j, _mm_add_ps(input[j], additive[j])); | ||||
|    } | ||||
| 
 | ||||
|    audio_mix_volume_C(out, in, vol, samples - i); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void audio_mix_free_chunk(audio_chunk_t *chunk) | ||||
| { | ||||
|    if (!chunk) | ||||
|       return; | ||||
| 
 | ||||
|    if (chunk->rwav && chunk->rwav->samples) | ||||
|    { | ||||
|       /* rwav_free only frees the samples */ | ||||
|       rwav_free(chunk->rwav); | ||||
|       free(chunk->rwav); | ||||
|    } | ||||
| 
 | ||||
|    if (chunk->buf) | ||||
|       free(chunk->buf); | ||||
| 
 | ||||
|    if (chunk->upsample_buf) | ||||
|       memalign_free(chunk->upsample_buf); | ||||
| 
 | ||||
|    if (chunk->float_buf) | ||||
|       memalign_free(chunk->float_buf); | ||||
| 
 | ||||
|    if (chunk->float_resample_buf) | ||||
|       memalign_free(chunk->float_resample_buf); | ||||
| 
 | ||||
|    if (chunk->resample_buf) | ||||
|       memalign_free(chunk->resample_buf); | ||||
| 
 | ||||
|    if (chunk->resampler && chunk->resampler_data) | ||||
|       chunk->resampler->free(chunk->resampler_data); | ||||
| 
 | ||||
|    free(chunk); | ||||
| } | ||||
| 
 | ||||
| audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate) | ||||
| { | ||||
|    int sample_size; | ||||
|    ssize_t len          = 0; | ||||
|    void *buf            = NULL; | ||||
|    audio_chunk_t *chunk = (audio_chunk_t*)calloc(1, sizeof(*chunk)); | ||||
| 
 | ||||
|    if (!chunk) | ||||
|       return NULL; | ||||
| 
 | ||||
|    if (!filestream_read_file(path, &buf, &len)) | ||||
|    { | ||||
|       printf("Could not open WAV file for reading.\n"); | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    chunk->sample_rate = sample_rate; | ||||
|    chunk->buf         = buf; | ||||
|    chunk->len         = len; | ||||
|    chunk->rwav        = (rwav_t*)malloc(sizeof(rwav_t)); | ||||
| 
 | ||||
|    if (rwav_load(chunk->rwav, chunk->buf, chunk->len) == RWAV_ITERATE_ERROR) | ||||
|    { | ||||
|       printf("error: could not load WAV file\n"); | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    /* numsamples does not know or care about
 | ||||
|     * multiple channels, but we need space for 2 */ | ||||
|    chunk->upsample_buf = (int16_t*)memalign_alloc(128, | ||||
|          chunk->rwav->numsamples * 2 * sizeof(int16_t)); | ||||
| 
 | ||||
|    sample_size = chunk->rwav->bitspersample / 8; | ||||
| 
 | ||||
|    if (sample_size == 1) | ||||
|    { | ||||
|       unsigned i; | ||||
| 
 | ||||
|      for (i = 0; i < chunk->rwav->numsamples; i++) | ||||
|      { | ||||
|         uint8_t *sample                     = ( | ||||
|               (uint8_t*)chunk->rwav->samples) + | ||||
|            (i * chunk->rwav->numchannels); | ||||
| 
 | ||||
|         chunk->upsample_buf[i * 2]          = (int16_t)((sample[0] - 128) << 8); | ||||
| 
 | ||||
|         if (chunk->rwav->numchannels == 1) | ||||
|            chunk->upsample_buf[(i * 2) + 1] = (int16_t)((sample[0] - 128) << 8); | ||||
|         else if (chunk->rwav->numchannels == 2) | ||||
|            chunk->upsample_buf[(i * 2) + 1] = (int16_t)((sample[1] - 128) << 8); | ||||
|      } | ||||
|    } | ||||
|    else if (sample_size == 2) | ||||
|    { | ||||
|       if (chunk->rwav->numchannels == 1) | ||||
|       { | ||||
|          unsigned i; | ||||
| 
 | ||||
|          for (i = 0; i < chunk->rwav->numsamples; i++) | ||||
|          { | ||||
|             int16_t sample                   = ((int16_t*)chunk->rwav->samples)[i]; | ||||
| 
 | ||||
|             chunk->upsample_buf[i * 2]       = sample; | ||||
|             chunk->upsample_buf[(i * 2) + 1] = sample; | ||||
|          } | ||||
|       } | ||||
|       else if (chunk->rwav->numchannels == 2) | ||||
|          memcpy(chunk->upsample_buf, chunk->rwav->samples, chunk->rwav->subchunk2size); | ||||
|    } | ||||
|    else if (sample_size != 2) | ||||
|    { | ||||
|       /* we don't support any other sample size besides 8 and 16-bit yet */ | ||||
|       printf("error: we don't support a sample size of %d\n", sample_size); | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    if (sample_rate != (int)chunk->rwav->samplerate) | ||||
|    { | ||||
|       chunk->resample = true; | ||||
|       chunk->ratio = (double)sample_rate / chunk->rwav->samplerate; | ||||
| 
 | ||||
|       retro_resampler_realloc(&chunk->resampler_data, | ||||
|             &chunk->resampler, | ||||
|             NULL, | ||||
|             RESAMPLER_QUALITY_DONTCARE, | ||||
|             chunk->ratio); | ||||
| 
 | ||||
|       if (chunk->resampler && chunk->resampler_data) | ||||
|       { | ||||
|          struct resampler_data info; | ||||
| 
 | ||||
|          chunk->float_buf = (float*)memalign_alloc(128, chunk->rwav->numsamples * 2 * chunk->ratio * sizeof(float)); | ||||
| 
 | ||||
|          /* why is *3 needed instead of just *2? does the sinc driver require more space than we know about? */ | ||||
|          chunk->float_resample_buf = (float*)memalign_alloc(128, chunk->rwav->numsamples * 3 * chunk->ratio * sizeof(float)); | ||||
| 
 | ||||
|          convert_s16_to_float(chunk->float_buf, chunk->upsample_buf, chunk->rwav->numsamples * 2, 1.0); | ||||
| 
 | ||||
|          info.data_in       = (const float*)chunk->float_buf; | ||||
|          info.data_out      = chunk->float_resample_buf; | ||||
|          /* a 'frame' consists of two channels, so we set this
 | ||||
|           * to the number of samples irrespective of channel count */ | ||||
|          info.input_frames  = chunk->rwav->numsamples; | ||||
|          info.output_frames = 0; | ||||
|          info.ratio         = chunk->ratio; | ||||
| 
 | ||||
|          chunk->resampler->process(chunk->resampler_data, &info); | ||||
| 
 | ||||
|          /* number of output_frames does not increase with multiple channels, but assume we need space for 2 */ | ||||
|          chunk->resample_buf = (int16_t*)memalign_alloc(128, info.output_frames * 2 * sizeof(int16_t)); | ||||
|          chunk->resample_len = info.output_frames; | ||||
|          convert_float_to_s16(chunk->resample_buf, chunk->float_resample_buf, info.output_frames * 2); | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    return chunk; | ||||
| 
 | ||||
| error: | ||||
|    audio_mix_free_chunk(chunk); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk) | ||||
| { | ||||
|    if (!chunk) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (chunk->rwav) | ||||
|    { | ||||
|       if (chunk->resample) | ||||
|          return chunk->resample_len; | ||||
|       return chunk->rwav->numsamples; | ||||
|    } | ||||
| 
 | ||||
|    /* no other filetypes supported yet */ | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * audio_mix_get_chunk_sample: | ||||
|  * @chunk              : audio chunk instance | ||||
|  * @channel            : channel of the sample (0=left, 1=right) | ||||
|  * @index              : index of the sample | ||||
|  * | ||||
|  * Get a sample from an audio chunk. | ||||
|  * | ||||
|  * Returns: A signed 16-bit audio sample. | ||||
|  **/ | ||||
| int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk, unsigned channel, size_t index) | ||||
| { | ||||
|    if (!chunk) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (chunk->rwav) | ||||
|    { | ||||
|       int sample_size    = chunk->rwav->bitspersample / 8; | ||||
|       int16_t sample_out = 0; | ||||
| 
 | ||||
|       /* 0 is the first/left channel */ | ||||
|       uint8_t *sample    = NULL; | ||||
| 
 | ||||
|       if (chunk->resample) | ||||
|          sample = (uint8_t*)chunk->resample_buf + | ||||
|             (sample_size * index * chunk->rwav->numchannels) + (channel * sample_size); | ||||
|       else | ||||
|          sample = (uint8_t*)chunk->upsample_buf + | ||||
|             (sample_size * index * chunk->rwav->numchannels) + (channel * sample_size); | ||||
| 
 | ||||
|       sample_out = (int16_t)*sample; | ||||
| 
 | ||||
|       return sample_out; | ||||
|    } | ||||
| 
 | ||||
|    /* no other filetypes supported yet */ | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk) | ||||
| { | ||||
|    if (!chunk) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (chunk->rwav) | ||||
|    { | ||||
|       int16_t *sample; | ||||
| 
 | ||||
|       if (chunk->resample) | ||||
|          sample = chunk->resample_buf; | ||||
|       else | ||||
|          sample = chunk->upsample_buf; | ||||
| 
 | ||||
|       return sample; | ||||
|    } | ||||
| 
 | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk) | ||||
| { | ||||
|    if (!chunk) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (chunk->rwav) | ||||
|       return chunk->rwav->numchannels; | ||||
| 
 | ||||
|    /* don't support other formats yet */ | ||||
|    return 0; | ||||
| } | ||||
|  | @ -0,0 +1,855 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (audio_mixer.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <audio/audio_mixer.h> | ||||
| #include <audio/audio_resampler.h> | ||||
| 
 | ||||
| #include <formats/rwav.h> | ||||
| #include <memalign.h> | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
| #include <rthreads/rthreads.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <math.h> | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "../../config.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_STB_VORBIS | ||||
| #define STB_VORBIS_NO_PUSHDATA_API | ||||
| #define STB_VORBIS_NO_STDIO | ||||
| #define STB_VORBIS_NO_CRT | ||||
| 
 | ||||
| #include <stb_vorbis.h> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_IBXM | ||||
| #include <ibxm/ibxm.h> | ||||
| #endif | ||||
| 
 | ||||
| #define AUDIO_MIXER_MAX_VOICES      8 | ||||
| #define AUDIO_MIXER_TEMP_OGG_BUFFER 8192 | ||||
| 
 | ||||
| struct audio_mixer_sound | ||||
| { | ||||
|    enum audio_mixer_type type; | ||||
| 
 | ||||
|    union | ||||
|    { | ||||
|       struct | ||||
|       { | ||||
|          /* wav */ | ||||
|          unsigned frames; | ||||
|          const float* pcm; | ||||
|       } wav; | ||||
| 
 | ||||
| #ifdef HAVE_STB_VORBIS | ||||
|       struct | ||||
|       { | ||||
|          /* ogg */ | ||||
|          unsigned size; | ||||
|          const void* data; | ||||
|       } ogg; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_IBXM | ||||
|       struct | ||||
|       { | ||||
|          /* mod/s3m/xm */ | ||||
|          unsigned size; | ||||
|          const void* data; | ||||
|       } mod; | ||||
| #endif | ||||
|    } types; | ||||
| }; | ||||
| 
 | ||||
| struct audio_mixer_voice | ||||
| { | ||||
|    bool     repeat; | ||||
|    unsigned type; | ||||
|    float    volume; | ||||
|    audio_mixer_sound_t *sound; | ||||
|    audio_mixer_stop_cb_t stop_cb; | ||||
| 
 | ||||
|    union | ||||
|    { | ||||
|       struct | ||||
|       { | ||||
|          unsigned position; | ||||
|       } wav; | ||||
| 
 | ||||
| #ifdef HAVE_STB_VORBIS | ||||
|       struct | ||||
|       { | ||||
|          unsigned    position; | ||||
|          unsigned    samples; | ||||
|          unsigned    buf_samples; | ||||
|          float*      buffer; | ||||
|          float       ratio; | ||||
|          stb_vorbis *stream; | ||||
|          void       *resampler_data; | ||||
|          const retro_resampler_t *resampler; | ||||
|       } ogg; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_IBXM | ||||
|       struct | ||||
|       { | ||||
|          unsigned    		position; | ||||
|          unsigned    		samples; | ||||
|          unsigned    		buf_samples; | ||||
|          int*               buffer; | ||||
|          struct replay*		stream; | ||||
|       } mod; | ||||
| #endif | ||||
|    } types; | ||||
| }; | ||||
| 
 | ||||
| static struct audio_mixer_voice s_voices[AUDIO_MIXER_MAX_VOICES]; | ||||
| static unsigned s_rate = 0; | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
| static slock_t* s_locker = NULL; | ||||
| #endif | ||||
| 
 | ||||
| static bool wav2float(const rwav_t* wav, float** pcm, size_t samples_out) | ||||
| { | ||||
|    size_t i; | ||||
|    /* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes */ | ||||
|    float *f           = (float*)memalign_alloc(16, | ||||
|          ((samples_out + 15) & ~15) * sizeof(float)); | ||||
| 
 | ||||
|    if (!f) | ||||
|       return false; | ||||
| 
 | ||||
|    *pcm = f; | ||||
| 
 | ||||
|    if (wav->bitspersample == 8) | ||||
|    { | ||||
|       float sample      = 0.0f; | ||||
|       const uint8_t *u8 = (const uint8_t*)wav->samples; | ||||
| 
 | ||||
|       if (wav->numchannels == 1) | ||||
|       { | ||||
|          for (i = wav->numsamples; i != 0; i--) | ||||
|          { | ||||
|             sample = (float)*u8++ / 255.0f; | ||||
|             sample = sample * 2.0f - 1.0f; | ||||
|             *f++   = sample; | ||||
|             *f++   = sample; | ||||
|          } | ||||
|       } | ||||
|       else if (wav->numchannels == 2) | ||||
|       { | ||||
|          for (i = wav->numsamples; i != 0; i--) | ||||
|          { | ||||
|             sample = (float)*u8++ / 255.0f; | ||||
|             sample = sample * 2.0f - 1.0f; | ||||
|             *f++   = sample; | ||||
|             sample = (float)*u8++ / 255.0f; | ||||
|             sample = sample * 2.0f - 1.0f; | ||||
|             *f++   = sample; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       /* TODO/FIXME note to leiradel - can we use audio/conversion/s16_to_float
 | ||||
|        * functions here? */ | ||||
| 
 | ||||
|       float sample       = 0.0f; | ||||
|       const int16_t *s16 = (const int16_t*)wav->samples; | ||||
| 
 | ||||
|       if (wav->numchannels == 1) | ||||
|       { | ||||
|          for (i = wav->numsamples; i != 0; i--) | ||||
|          { | ||||
|             sample = (float)((int)*s16++ + 32768) / 65535.0f; | ||||
|             sample = sample * 2.0f - 1.0f; | ||||
|             *f++   = sample; | ||||
|             *f++   = sample; | ||||
|          } | ||||
|       } | ||||
|       else if (wav->numchannels == 2) | ||||
|       { | ||||
|          for (i = wav->numsamples; i != 0; i--) | ||||
|          { | ||||
|             sample = (float)((int)*s16++ + 32768) / 65535.0f; | ||||
|             sample = sample * 2.0f - 1.0f; | ||||
|             *f++   = sample; | ||||
|             sample = (float)((int)*s16++ + 32768) / 65535.0f; | ||||
|             sample = sample * 2.0f - 1.0f; | ||||
|             *f++   = sample; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static bool one_shot_resample(const float* in, size_t samples_in, | ||||
|       unsigned rate, float** out, size_t* samples_out) | ||||
| { | ||||
|    struct resampler_data info; | ||||
|    void* data                         = NULL; | ||||
|    const retro_resampler_t* resampler = NULL; | ||||
|    float ratio                        = (double)s_rate / (double)rate; | ||||
| 
 | ||||
|    if (!retro_resampler_realloc(&data, &resampler, NULL,  | ||||
|             RESAMPLER_QUALITY_DONTCARE, ratio)) | ||||
|       return false; | ||||
| 
 | ||||
|    /*
 | ||||
|     * Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes. We | ||||
|     * add four more samples in the formula below just as safeguard, because | ||||
|     * resampler->process sometimes reports more output samples than the | ||||
|     * formula below calculates. Ideally, audio resamplers should have a | ||||
|     * function to return the number of samples they will output given a | ||||
|     * count of input samples. | ||||
|     */ | ||||
|    *samples_out                       = samples_in * ratio + 4; | ||||
|    *out                               = (float*)memalign_alloc(16, | ||||
|          ((*samples_out + 15) & ~15) * sizeof(float)); | ||||
| 
 | ||||
|    if (*out == NULL) | ||||
|       return false; | ||||
| 
 | ||||
|    info.data_in                       = in; | ||||
|    info.data_out                      = *out; | ||||
|    info.input_frames                  = samples_in / 2; | ||||
|    info.output_frames                 = 0; | ||||
|    info.ratio                         = ratio; | ||||
| 
 | ||||
|    resampler->process(data, &info); | ||||
|    resampler->free(data); | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| void audio_mixer_init(unsigned rate) | ||||
| { | ||||
|    unsigned i; | ||||
| 
 | ||||
|    s_rate = rate; | ||||
| 
 | ||||
|    for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++) | ||||
|       s_voices[i].type = AUDIO_MIXER_TYPE_NONE; | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
|    s_locker = slock_new(); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void audio_mixer_done(void) | ||||
| { | ||||
|    unsigned i; | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
|    /* Dont call audio mixer functions after this point */ | ||||
|    slock_free(s_locker); | ||||
|    s_locker = NULL; | ||||
| #endif | ||||
| 
 | ||||
|    for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++) | ||||
|       s_voices[i].type = AUDIO_MIXER_TYPE_NONE; | ||||
| } | ||||
| 
 | ||||
| audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size) | ||||
| { | ||||
|    /* WAV data */ | ||||
|    rwav_t wav; | ||||
|    /* WAV samples converted to float */ | ||||
|    float* pcm                 = NULL; | ||||
|    size_t samples             = 0; | ||||
|    /* Result */ | ||||
|    audio_mixer_sound_t* sound = NULL; | ||||
|    enum rwav_state rwav_ret   = rwav_load(&wav, buffer, size); | ||||
| 
 | ||||
|    if (rwav_ret != RWAV_ITERATE_DONE) | ||||
|       return NULL; | ||||
| 
 | ||||
|    samples       = wav.numsamples * 2; | ||||
| 
 | ||||
|    if (!wav2float(&wav, &pcm, samples)) | ||||
|       return NULL; | ||||
| 
 | ||||
|    if (wav.samplerate != s_rate) | ||||
|    { | ||||
|       float* resampled           = NULL; | ||||
| 
 | ||||
|       if (!one_shot_resample(pcm, samples, | ||||
|                wav.samplerate, &resampled, &samples)) | ||||
|          return NULL; | ||||
| 
 | ||||
|       memalign_free((void*)pcm); | ||||
|       pcm = resampled; | ||||
|    } | ||||
| 
 | ||||
|    sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound)); | ||||
| 
 | ||||
|    if (!sound) | ||||
|    { | ||||
|       memalign_free((void*)pcm); | ||||
|       return NULL; | ||||
|    } | ||||
| 
 | ||||
|    sound->type             = AUDIO_MIXER_TYPE_WAV; | ||||
|    sound->types.wav.frames = (unsigned)(samples / 2); | ||||
|    sound->types.wav.pcm    = pcm; | ||||
| 
 | ||||
|    rwav_free(&wav); | ||||
| 
 | ||||
|    return sound; | ||||
| } | ||||
| 
 | ||||
| audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size) | ||||
| { | ||||
| #ifdef HAVE_STB_VORBIS | ||||
|    audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound)); | ||||
| 
 | ||||
|    if (!sound) | ||||
|       return NULL; | ||||
| 
 | ||||
|    sound->type           = AUDIO_MIXER_TYPE_OGG; | ||||
|    sound->types.ogg.size = size; | ||||
|    sound->types.ogg.data = buffer; | ||||
| 
 | ||||
|    return sound; | ||||
| #else | ||||
|    return NULL; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size) | ||||
| { | ||||
| #ifdef HAVE_IBXM | ||||
|    audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound)); | ||||
| 
 | ||||
|    if (!sound) | ||||
|       return NULL; | ||||
| 
 | ||||
|    sound->type           = AUDIO_MIXER_TYPE_MOD; | ||||
|    sound->types.mod.size = size; | ||||
|    sound->types.mod.data = buffer; | ||||
| 
 | ||||
|    return sound; | ||||
| #else | ||||
|    return NULL; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void audio_mixer_destroy(audio_mixer_sound_t* sound) | ||||
| { | ||||
|    void *handle = NULL; | ||||
|    if (!sound) | ||||
|       return; | ||||
| 
 | ||||
|    switch (sound->type) | ||||
|    { | ||||
|       case AUDIO_MIXER_TYPE_WAV: | ||||
|          handle = (void*)sound->types.wav.pcm; | ||||
|          if (handle) | ||||
|             memalign_free(handle); | ||||
|          break; | ||||
|       case AUDIO_MIXER_TYPE_OGG: | ||||
| #ifdef HAVE_STB_VORBIS | ||||
|          handle = (void*)sound->types.ogg.data; | ||||
|          if (handle) | ||||
|             free(handle); | ||||
| #endif | ||||
|          break; | ||||
|       case AUDIO_MIXER_TYPE_MOD: | ||||
| #ifdef HAVE_IBXM | ||||
|          handle = (void*)sound->types.mod.data; | ||||
|          if (handle) | ||||
|             free(handle); | ||||
| #endif | ||||
|          break; | ||||
|       case AUDIO_MIXER_TYPE_NONE: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    free(sound); | ||||
| } | ||||
| 
 | ||||
| static bool audio_mixer_play_wav(audio_mixer_sound_t* sound, | ||||
|       audio_mixer_voice_t* voice, bool repeat, float volume, | ||||
|       audio_mixer_stop_cb_t stop_cb) | ||||
| { | ||||
|    voice->types.wav.position = 0; | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| #ifdef HAVE_STB_VORBIS | ||||
| static bool audio_mixer_play_ogg( | ||||
|       audio_mixer_sound_t* sound, | ||||
|       audio_mixer_voice_t* voice, | ||||
|       bool repeat, float volume, | ||||
|       audio_mixer_stop_cb_t stop_cb) | ||||
| { | ||||
|    stb_vorbis_info info; | ||||
|    int res                         = 0; | ||||
|    float ratio                     = 0.0f; | ||||
|    unsigned samples                = 0; | ||||
|    void *ogg_buffer                = NULL; | ||||
|    void *resampler_data            = NULL; | ||||
|    const retro_resampler_t* resamp = NULL; | ||||
|    stb_vorbis *stb_vorbis          = stb_vorbis_open_memory( | ||||
|          (const unsigned char*)sound->types.ogg.data, | ||||
|          sound->types.ogg.size, &res, NULL); | ||||
| 
 | ||||
|    if (!stb_vorbis) | ||||
|       return false; | ||||
| 
 | ||||
|    info                    = stb_vorbis_get_info(stb_vorbis); | ||||
| 
 | ||||
|    if (info.sample_rate != s_rate) | ||||
|    { | ||||
|       ratio = (double)s_rate / (double)info.sample_rate; | ||||
| 
 | ||||
|       if (!retro_resampler_realloc(&resampler_data, | ||||
|                &resamp, NULL, RESAMPLER_QUALITY_DONTCARE, | ||||
|                ratio)) | ||||
|          goto error; | ||||
|    } | ||||
| 
 | ||||
|    samples                         = (unsigned)(AUDIO_MIXER_TEMP_OGG_BUFFER * ratio); | ||||
|    ogg_buffer                      = (float*)memalign_alloc(16, | ||||
|          ((samples + 15) & ~15) * sizeof(float)); | ||||
| 
 | ||||
|    if (!ogg_buffer) | ||||
|    { | ||||
|       resamp->free(resampler_data); | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    voice->types.ogg.resampler      = resamp; | ||||
|    voice->types.ogg.resampler_data = resampler_data; | ||||
|    voice->types.ogg.buffer         = (float*)ogg_buffer; | ||||
|    voice->types.ogg.buf_samples    = samples; | ||||
|    voice->types.ogg.ratio          = ratio; | ||||
|    voice->types.ogg.stream         = stb_vorbis; | ||||
|    voice->types.ogg.position       = 0; | ||||
|    voice->types.ogg.samples        = 0; | ||||
| 
 | ||||
|    return true; | ||||
| 
 | ||||
| error: | ||||
|    stb_vorbis_close(stb_vorbis); | ||||
|    return false; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_IBXM | ||||
| static bool audio_mixer_play_mod( | ||||
|       audio_mixer_sound_t* sound, | ||||
|       audio_mixer_voice_t* voice, | ||||
|       bool repeat, float volume, | ||||
|       audio_mixer_stop_cb_t stop_cb) | ||||
| { | ||||
|    struct data data; | ||||
|    char message[64]; | ||||
|    int buf_samples               = 0; | ||||
|    int samples                   = 0; | ||||
|    void *mod_buffer              = NULL; | ||||
|    struct module* module         = NULL; | ||||
|    struct replay* replay         = NULL; | ||||
| 
 | ||||
|    data.buffer                   = (char*)sound->types.mod.data; | ||||
|    data.length                   = sound->types.mod.size; | ||||
|    module                        = module_load(&data, message); | ||||
| 
 | ||||
|    if (!module) | ||||
|    { | ||||
|       printf("audio_mixer_play_mod module_load() failed with error: %s\n", message); | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    replay = new_replay( module, s_rate, 1); | ||||
| 
 | ||||
|    if (!replay) | ||||
|    { | ||||
|       printf("audio_mixer_play_mod new_replay() failed\n"); | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    buf_samples = calculate_mix_buf_len(s_rate); | ||||
|    mod_buffer  = memalign_alloc(16, ((buf_samples + 15) & ~15) * sizeof(int)); | ||||
| 
 | ||||
|    if (!mod_buffer) | ||||
|    { | ||||
|       printf("audio_mixer_play_mod cannot allocate mod_buffer !\n"); | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    samples = replay_calculate_duration(replay); | ||||
| 
 | ||||
|    if (!samples) | ||||
|    { | ||||
|       printf("audio_mixer_play_mod cannot retrieve duration !\n"); | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    voice->types.mod.buffer         = (int*)mod_buffer; | ||||
|    voice->types.mod.buf_samples    = buf_samples; | ||||
|    voice->types.mod.stream         = replay; | ||||
|    voice->types.mod.position       = 0; | ||||
|     voice->types.mod.samples       = 0; /* samples; */ | ||||
| 
 | ||||
|    return true; | ||||
| 
 | ||||
| error: | ||||
|    if (mod_buffer) | ||||
|       memalign_free(mod_buffer); | ||||
|    if (module) | ||||
|       dispose_module(module); | ||||
|    return false; | ||||
| 
 | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat, | ||||
|       float volume, audio_mixer_stop_cb_t stop_cb) | ||||
| { | ||||
|    unsigned i; | ||||
|    bool res                   = false; | ||||
|    audio_mixer_voice_t* voice = s_voices; | ||||
| 
 | ||||
|    if (!sound) | ||||
|       return NULL; | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
|    slock_lock(s_locker); | ||||
| #endif | ||||
| 
 | ||||
|    for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++, voice++) | ||||
|    { | ||||
|       if (voice->type != AUDIO_MIXER_TYPE_NONE) | ||||
|          continue; | ||||
| 
 | ||||
|       switch (sound->type) | ||||
|       { | ||||
|          case AUDIO_MIXER_TYPE_WAV: | ||||
|             res = audio_mixer_play_wav(sound, voice, repeat, volume, stop_cb); | ||||
|             break; | ||||
|          case AUDIO_MIXER_TYPE_OGG: | ||||
| #ifdef HAVE_STB_VORBIS | ||||
|             res = audio_mixer_play_ogg(sound, voice, repeat, volume, stop_cb); | ||||
| #endif | ||||
|             break; | ||||
|          case AUDIO_MIXER_TYPE_MOD: | ||||
| #ifdef HAVE_IBXM | ||||
|             res = audio_mixer_play_mod(sound, voice, repeat, volume, stop_cb); | ||||
| #endif | ||||
|             break; | ||||
|          case AUDIO_MIXER_TYPE_NONE: | ||||
|             break; | ||||
|       } | ||||
| 
 | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    if (res) | ||||
|    { | ||||
|       voice->type     = sound->type; | ||||
|       voice->repeat   = repeat; | ||||
|       voice->volume   = volume; | ||||
|       voice->sound    = sound; | ||||
|       voice->stop_cb  = stop_cb; | ||||
|    } | ||||
|    else | ||||
|       voice = NULL; | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
|    slock_unlock(s_locker); | ||||
| #endif | ||||
| 
 | ||||
|    return voice; | ||||
| } | ||||
| 
 | ||||
| void audio_mixer_stop(audio_mixer_voice_t* voice) | ||||
| { | ||||
|    audio_mixer_stop_cb_t stop_cb = NULL; | ||||
|    audio_mixer_sound_t* sound    = NULL; | ||||
| 
 | ||||
|    if (voice) | ||||
|    { | ||||
|       stop_cb = voice->stop_cb; | ||||
|       sound   = voice->sound; | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
|       slock_lock(s_locker); | ||||
| #endif | ||||
| 
 | ||||
|       voice->type = AUDIO_MIXER_TYPE_NONE; | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
|       slock_unlock(s_locker); | ||||
| #endif | ||||
| 
 | ||||
|       if (stop_cb) | ||||
|          stop_cb(sound, AUDIO_MIXER_SOUND_STOPPED); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void audio_mixer_mix_wav(float* buffer, size_t num_frames, | ||||
|       audio_mixer_voice_t* voice, | ||||
|       float volume) | ||||
| { | ||||
|    int i; | ||||
|    unsigned buf_free                = (unsigned)(num_frames * 2); | ||||
|    const audio_mixer_sound_t* sound = voice->sound; | ||||
|    unsigned pcm_available           = sound->types.wav.frames | ||||
|       * 2 - voice->types.wav.position; | ||||
|    const float* pcm                 = sound->types.wav.pcm + | ||||
|       voice->types.wav.position; | ||||
| 
 | ||||
| again: | ||||
|    if (pcm_available < buf_free) | ||||
|    { | ||||
|       for (i = pcm_available; i != 0; i--) | ||||
|          *buffer++ += *pcm++ * volume; | ||||
| 
 | ||||
|       if (voice->repeat) | ||||
|       { | ||||
|          if (voice->stop_cb) | ||||
|             voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED); | ||||
| 
 | ||||
|          buf_free                  -= pcm_available; | ||||
|          pcm_available              = sound->types.wav.frames * 2; | ||||
|          pcm                        = sound->types.wav.pcm; | ||||
|          voice->types.wav.position  = 0; | ||||
|          goto again; | ||||
|       } | ||||
| 
 | ||||
|       if (voice->stop_cb) | ||||
|          voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED); | ||||
| 
 | ||||
|       voice->type = AUDIO_MIXER_TYPE_NONE; | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       for (i = buf_free; i != 0; i--) | ||||
|          *buffer++ += *pcm++ * volume; | ||||
| 
 | ||||
|       voice->types.wav.position += buf_free; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| #ifdef HAVE_STB_VORBIS | ||||
| static void audio_mixer_mix_ogg(float* buffer, size_t num_frames, | ||||
|       audio_mixer_voice_t* voice, | ||||
|       float volume) | ||||
| { | ||||
|    int i; | ||||
|    struct resampler_data info; | ||||
|    float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER]; | ||||
|    unsigned buf_free                = num_frames * 2; | ||||
|    unsigned temp_samples            = 0; | ||||
|    float* pcm                       = NULL; | ||||
| 
 | ||||
|    if (voice->types.ogg.position == voice->types.ogg.samples) | ||||
|    { | ||||
| again: | ||||
|       temp_samples = stb_vorbis_get_samples_float_interleaved( | ||||
|             voice->types.ogg.stream, 2, temp_buffer, | ||||
|             AUDIO_MIXER_TEMP_OGG_BUFFER) * 2; | ||||
| 
 | ||||
|       if (temp_samples == 0) | ||||
|       { | ||||
|          if (voice->repeat) | ||||
|          { | ||||
|             if (voice->stop_cb) | ||||
|                voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED); | ||||
| 
 | ||||
|             stb_vorbis_seek_start(voice->types.ogg.stream); | ||||
|             goto again; | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             if (voice->stop_cb) | ||||
|                voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED); | ||||
| 
 | ||||
|             voice->type = AUDIO_MIXER_TYPE_NONE; | ||||
|             return; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       info.data_in              = temp_buffer; | ||||
|       info.data_out             = voice->types.ogg.buffer; | ||||
|       info.input_frames         = temp_samples / 2; | ||||
|       info.output_frames        = 0; | ||||
|       info.ratio                = voice->types.ogg.ratio; | ||||
| 
 | ||||
|       voice->types.ogg.resampler->process(voice->types.ogg.resampler_data, &info); | ||||
|       voice->types.ogg.position = 0; | ||||
|       voice->types.ogg.samples  = voice->types.ogg.buf_samples; | ||||
|    } | ||||
| 
 | ||||
|    pcm = voice->types.ogg.buffer + voice->types.ogg.position; | ||||
| 
 | ||||
|    if (voice->types.ogg.samples < buf_free) | ||||
|    { | ||||
|       for (i = voice->types.ogg.samples; i != 0; i--) | ||||
|          *buffer++ += *pcm++ * volume; | ||||
| 
 | ||||
|       buf_free -= voice->types.ogg.samples; | ||||
|       goto again; | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       int i; | ||||
|       for (i = buf_free; i != 0; --i ) | ||||
|          *buffer++ += *pcm++ * volume; | ||||
| 
 | ||||
|       voice->types.ogg.position += buf_free; | ||||
|       voice->types.ogg.samples  -= buf_free; | ||||
|    } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_IBXM | ||||
| static void audio_mixer_mix_mod(float* buffer, size_t num_frames, | ||||
|       audio_mixer_voice_t* voice, | ||||
|       float volume) | ||||
| { | ||||
|    int i; | ||||
|    float samplef                    = 0.0f; | ||||
|    int samplei                      = 0; | ||||
|    unsigned temp_samples            = 0; | ||||
|    unsigned buf_free                = num_frames * 2; | ||||
|    int* pcm                         = NULL; | ||||
| 
 | ||||
|    if (voice->types.mod.position == voice->types.mod.samples) | ||||
|    { | ||||
| again: | ||||
|       temp_samples = replay_get_audio( | ||||
|             voice->types.mod.stream, voice->types.mod.buffer ); | ||||
| 
 | ||||
|       temp_samples *= 2; /* stereo */ | ||||
| 
 | ||||
|       if (temp_samples == 0) | ||||
|       { | ||||
|          if (voice->repeat) | ||||
|          { | ||||
|             if (voice->stop_cb) | ||||
|                voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED); | ||||
| 
 | ||||
|             replay_seek( voice->types.mod.stream, 0); | ||||
|             goto again; | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             if (voice->stop_cb) | ||||
|                voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED); | ||||
| 
 | ||||
|             voice->type = AUDIO_MIXER_TYPE_NONE; | ||||
|             return; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       voice->types.mod.position = 0; | ||||
|       voice->types.mod.samples  = temp_samples; | ||||
|    } | ||||
|    pcm = voice->types.mod.buffer + voice->types.mod.position; | ||||
| 
 | ||||
|    if (voice->types.mod.samples < buf_free) | ||||
|    { | ||||
|       for (i = voice->types.mod.samples; i != 0; i--) | ||||
|       { | ||||
|          samplei     = *pcm++ * volume; | ||||
|          samplef     = (float)((int)samplei + 32768) / 65535.0f; | ||||
|          samplef     = samplef * 2.0f - 1.0f; | ||||
|          *buffer++  += samplef; | ||||
|       } | ||||
| 
 | ||||
|       buf_free -= voice->types.mod.samples; | ||||
|       goto again; | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       int i; | ||||
|       for (i = buf_free; i != 0; --i ) | ||||
|       { | ||||
|          samplei     = *pcm++ * volume; | ||||
|          samplef     = (float)((int)samplei + 32768) / 65535.0f; | ||||
|          samplef     = samplef * 2.0f - 1.0f; | ||||
|          *buffer++  += samplef; | ||||
|       } | ||||
| 
 | ||||
|       voice->types.mod.position += buf_free; | ||||
|       voice->types.mod.samples  -= buf_free; | ||||
|    } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bool override) | ||||
| { | ||||
|    unsigned i; | ||||
|    size_t j                   = 0; | ||||
|    float* sample              = NULL; | ||||
|    audio_mixer_voice_t* voice = s_voices; | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
|    slock_lock(s_locker); | ||||
| #endif | ||||
| 
 | ||||
|    for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++, voice++) | ||||
|    { | ||||
|       float volume = (override) ? volume_override : voice->volume; | ||||
| 
 | ||||
|       switch (voice->type) | ||||
|       { | ||||
|          case AUDIO_MIXER_TYPE_WAV: | ||||
|             audio_mixer_mix_wav(buffer, num_frames, voice, volume); | ||||
|             break; | ||||
|          case AUDIO_MIXER_TYPE_OGG: | ||||
| #ifdef HAVE_STB_VORBIS | ||||
|             audio_mixer_mix_ogg(buffer, num_frames, voice, volume); | ||||
| #endif | ||||
|             break; | ||||
|          case AUDIO_MIXER_TYPE_MOD: | ||||
| #ifdef HAVE_IBXM | ||||
|             audio_mixer_mix_mod(buffer, num_frames, voice, volume); | ||||
| #endif | ||||
|             break; | ||||
|          case AUDIO_MIXER_TYPE_NONE: | ||||
|             break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
| #ifdef HAVE_THREADS | ||||
|    slock_unlock(s_locker); | ||||
| #endif | ||||
| 
 | ||||
|    for (j = 0, sample = buffer; j < num_frames; j++, sample++) | ||||
|    { | ||||
|       if (*sample < -1.0f) | ||||
|          *sample = -1.0f; | ||||
|       else if (*sample > 1.0f) | ||||
|          *sample = 1.0f; | ||||
|    } | ||||
| } | ||||
|  | @ -0,0 +1,160 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (float_to_s16.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
| #include <emmintrin.h> | ||||
| #elif defined(__ALTIVEC__) | ||||
| #include <altivec.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <features/features_cpu.h> | ||||
| #include <audio/conversion/float_to_s16.h> | ||||
| 
 | ||||
| #if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS) | ||||
| static bool float_to_s16_neon_enabled = false; | ||||
| void convert_float_s16_asm(int16_t *out, const float *in, size_t samples); | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * convert_float_to_s16: | ||||
|  * @out               : output buffer | ||||
|  * @in                : input buffer | ||||
|  * @samples           : size of samples to be converted | ||||
|  * | ||||
|  * Converts floating point | ||||
|  * to signed integer 16-bit. | ||||
|  * | ||||
|  * C implementation callback function. | ||||
|  **/ | ||||
| void convert_float_to_s16(int16_t *out, | ||||
|       const float *in, size_t samples) | ||||
| { | ||||
|    size_t i      = 0; | ||||
| #if defined(__SSE2__) | ||||
|    __m128 factor = _mm_set1_ps((float)0x8000); | ||||
| 
 | ||||
|    for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8) | ||||
|    { | ||||
|       __m128 input_l = _mm_loadu_ps(in + 0); | ||||
|       __m128 input_r = _mm_loadu_ps(in + 4); | ||||
|       __m128 res_l   = _mm_mul_ps(input_l, factor); | ||||
|       __m128 res_r   = _mm_mul_ps(input_r, factor); | ||||
|       __m128i ints_l = _mm_cvtps_epi32(res_l); | ||||
|       __m128i ints_r = _mm_cvtps_epi32(res_r); | ||||
|       __m128i packed = _mm_packs_epi32(ints_l, ints_r); | ||||
| 
 | ||||
|       _mm_storeu_si128((__m128i *)out, packed); | ||||
|    } | ||||
| 
 | ||||
|    samples = samples - i; | ||||
|    i       = 0; | ||||
| #elif defined(__ALTIVEC__) | ||||
|    int samples_in = samples; | ||||
| 
 | ||||
|    /* Unaligned loads/store is a bit expensive,
 | ||||
|     * so we optimize for the good path (very likely). */ | ||||
|    if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0) | ||||
|    { | ||||
|       size_t i; | ||||
|       for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8) | ||||
|       { | ||||
|          vector float       input0 = vec_ld( 0, in); | ||||
|          vector float       input1 = vec_ld(16, in); | ||||
|          vector signed int result0 = vec_cts(input0, 15); | ||||
|          vector signed int result1 = vec_cts(input1, 15); | ||||
|          vec_st(vec_packs(result0, result1), 0, out); | ||||
|       } | ||||
| 
 | ||||
|       samples_in -= i; | ||||
|    } | ||||
| 
 | ||||
|    samples = samples_in; | ||||
|    i       = 0; | ||||
| #elif defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS) | ||||
|    if (float_to_s16_neon_enabled) | ||||
|    { | ||||
|       size_t aligned_samples = samples & ~7; | ||||
|       if (aligned_samples) | ||||
|          convert_float_s16_asm(out, in, aligned_samples); | ||||
| 
 | ||||
|       out     = out     + aligned_samples; | ||||
|       in      = in      + aligned_samples; | ||||
|       samples = samples - aligned_samples; | ||||
|       i       = 0; | ||||
|    } | ||||
| #elif defined(_MIPS_ARCH_ALLEGREX) | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
|    /* Make sure the buffers are 16 byte aligned, this should be
 | ||||
|     * the default behaviour of malloc in the PSPSDK. | ||||
|     * Assume alignment. */ | ||||
|    retro_assert(((uintptr_t)in  & 0xf) == 0); | ||||
|    retro_assert(((uintptr_t)out & 0xf) == 0); | ||||
| #endif | ||||
| 
 | ||||
|    for (i = 0; i + 8 <= samples; i += 8) | ||||
|    { | ||||
|       __asm__ ( | ||||
|             ".set    push                 \n" | ||||
|             ".set    noreorder            \n" | ||||
| 
 | ||||
|             "lv.q    c100,  0(%0)         \n" | ||||
|             "lv.q    c110,  16(%0)        \n" | ||||
| 
 | ||||
|             "vf2in.q c100, c100, 31       \n" | ||||
|             "vf2in.q c110, c110, 31       \n" | ||||
|             "vi2s.q  c100, c100           \n" | ||||
|             "vi2s.q  c102, c110           \n" | ||||
| 
 | ||||
|             "sv.q    c100,  0(%1)         \n" | ||||
| 
 | ||||
|             ".set    pop                  \n" | ||||
|             :: "r"(in + i), "r"(out + i)); | ||||
|    } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|    for (; i < samples; i++) | ||||
|    { | ||||
|       int32_t val = (int32_t)(in[i] * 0x8000); | ||||
|       out[i]      = (val > 0x7FFF) ? 0x7FFF : | ||||
|          (val < -0x8000 ? -0x8000 : (int16_t)val); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * convert_float_to_s16_init_simd: | ||||
|  * | ||||
|  * Sets up function pointers for conversion | ||||
|  * functions based on CPU features. | ||||
|  **/ | ||||
| void convert_float_to_s16_init_simd(void) | ||||
| { | ||||
| #if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS) | ||||
|    unsigned cpu = cpu_features_get(); | ||||
| 
 | ||||
|    if (cpu & RETRO_SIMD_NEON) | ||||
|       float_to_s16_neon_enabled = true; | ||||
| #endif | ||||
| } | ||||
|  | @ -0,0 +1,69 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (float_to_s16_neon.S). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #if defined(__ARM_NEON__) | ||||
| 
 | ||||
| #ifndef __MACH__ | ||||
| .arm | ||||
| #endif | ||||
| 
 | ||||
| .align 4
 | ||||
| .globl convert_float_s16_asm
 | ||||
| #ifndef __MACH__ | ||||
| .type convert_float_s16_asm, %function | ||||
| #endif | ||||
| .globl _convert_float_s16_asm
 | ||||
| #ifndef __MACH__ | ||||
| .type _convert_float_s16_asm, %function | ||||
| #endif | ||||
| # convert_float_s16_asm(int16_t *out, const float *in, size_t samples) | ||||
| convert_float_s16_asm: | ||||
| _convert_float_s16_asm: | ||||
|    # Hacky way to get a constant of 2^15. | ||||
|    # ((2^4)^2)^2 * 0.5 = 2^15 | ||||
|    vmov.f32 q8, #16.0 | ||||
|    vmov.f32 q9, #0.5 | ||||
|    vmul.f32 q8, q8, q8 | ||||
|    vmul.f32 q8, q8, q8 | ||||
|    vmul.f32 q8, q8, q9 | ||||
| 
 | ||||
| 1: | ||||
|    # Preload here? | ||||
|    vld1.f32 {q0-q1}, [r1]! | ||||
| 
 | ||||
|    vmul.f32 q0, q0, q8 | ||||
|    vmul.f32 q1, q1, q8 | ||||
| 
 | ||||
|    vcvt.s32.f32 q0, q0 | ||||
|    vcvt.s32.f32 q1, q1 | ||||
| 
 | ||||
|    vqmovn.s32 d4, q0 | ||||
|    vqmovn.s32 d5, q1 | ||||
| 
 | ||||
|    vst1.f32 {d4-d5}, [r0]! | ||||
| 
 | ||||
|    # Guaranteed to get samples in multiples of 8. | ||||
|    subs r2, r2, #8 | ||||
|    bne 1b | ||||
| 
 | ||||
|    bx lr | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,64 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (float_to_s16_neon.S). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #if defined(__ARM_NEON__) | ||||
| 
 | ||||
| #if defined(__thumb__) | ||||
| #define DECL_ARMMODE(x) "  .align 2\n" "  .global " x "\n" "  .thumb\n" "  .thumb_func\n" "  .type " x ", %function\n" x ":\n" | ||||
| #else | ||||
| #define DECL_ARMMODE(x) "  .align 4\n" "  .global " x "\n" "  .arm\n" x ":\n" | ||||
| #endif | ||||
| 
 | ||||
| asm( | ||||
|     DECL_ARMMODE("convert_float_s16_asm") | ||||
|     DECL_ARMMODE("_convert_float_s16_asm") | ||||
|     "# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)\n" | ||||
|     "   # Hacky way to get a constant of 2^15.\n" | ||||
|     "   # ((2^4)^2)^2 * 0.5 = 2^15\n" | ||||
|     "   vmov.f32 q8, #16.0\n" | ||||
|     "   vmov.f32 q9, #0.5\n" | ||||
|     "   vmul.f32 q8, q8, q8\n" | ||||
|     "   vmul.f32 q8, q8, q8\n" | ||||
|     "   vmul.f32 q8, q8, q9\n" | ||||
|     "\n" | ||||
|     "1:\n" | ||||
|     "   # Preload here?\n" | ||||
|     "   vld1.f32 {q0-q1}, [r1]!\n" | ||||
|     "\n" | ||||
|     "   vmul.f32 q0, q0, q8\n" | ||||
|     "   vmul.f32 q1, q1, q8\n" | ||||
|     "\n" | ||||
|     "   vcvt.s32.f32 q0, q0\n" | ||||
|     "   vcvt.s32.f32 q1, q1\n" | ||||
|     "\n" | ||||
|     "   vqmovn.s32 d4, q0\n" | ||||
|     "   vqmovn.s32 d5, q1\n" | ||||
|     "\n" | ||||
|     "   vst1.f32 {d4-d5}, [r0]!\n" | ||||
|     "\n" | ||||
|     "   # Guaranteed to get samples in multiples of 8.\n" | ||||
|     "   subs r2, r2, #8\n" | ||||
|     "   bne 1b\n" | ||||
|     "\n" | ||||
|     "   bx lr\n" | ||||
|     "\n" | ||||
|     ); | ||||
| #endif | ||||
|  | @ -0,0 +1,189 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (s16_to_float.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #if defined(__SSE2__) | ||||
| #include <emmintrin.h> | ||||
| #elif defined(__ALTIVEC__) | ||||
| #include <altivec.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <boolean.h> | ||||
| #include <features/features_cpu.h> | ||||
| #include <audio/conversion/s16_to_float.h> | ||||
| 
 | ||||
| #if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS) | ||||
| static bool s16_to_float_neon_enabled = false; | ||||
| 
 | ||||
| /* Avoid potential hard-float/soft-float ABI issues. */ | ||||
| void convert_s16_float_asm(float *out, const int16_t *in, | ||||
|       size_t samples, const float *gain); | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * convert_s16_to_float: | ||||
|  * @out               : output buffer | ||||
|  * @in                : input buffer | ||||
|  * @samples           : size of samples to be converted | ||||
|  * @gain              : gain applied (.e.g. audio volume) | ||||
|  * | ||||
|  * Converts from signed integer 16-bit | ||||
|  * to floating point. | ||||
|  **/ | ||||
| void convert_s16_to_float(float *out, | ||||
|       const int16_t *in, size_t samples, float gain) | ||||
| { | ||||
|    size_t i      = 0; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|    float fgain   = gain / UINT32_C(0x80000000); | ||||
|    __m128 factor = _mm_set1_ps(fgain); | ||||
| 
 | ||||
|    for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8) | ||||
|    { | ||||
|       __m128i input    = _mm_loadu_si128((const __m128i *)in); | ||||
|       __m128i regs_l   = _mm_unpacklo_epi16(_mm_setzero_si128(), input); | ||||
|       __m128i regs_r   = _mm_unpackhi_epi16(_mm_setzero_si128(), input); | ||||
|       __m128 output_l  = _mm_mul_ps(_mm_cvtepi32_ps(regs_l), factor); | ||||
|       __m128 output_r  = _mm_mul_ps(_mm_cvtepi32_ps(regs_r), factor); | ||||
| 
 | ||||
|       _mm_storeu_ps(out + 0, output_l); | ||||
|       _mm_storeu_ps(out + 4, output_r); | ||||
|    } | ||||
| 
 | ||||
|    samples = samples - i; | ||||
|    i       = 0; | ||||
| #elif defined(__ALTIVEC__) | ||||
|    size_t samples_in = samples; | ||||
| 
 | ||||
|    /* Unaligned loads/store is a bit expensive, so we
 | ||||
|     * optimize for the good path (very likely). */ | ||||
|    if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0) | ||||
|    { | ||||
|       size_t i; | ||||
|       const vector float gain_vec = { gain, gain , gain, gain }; | ||||
|       const vector float zero_vec = { 0.0f, 0.0f, 0.0f, 0.0f}; | ||||
| 
 | ||||
|       for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8) | ||||
|       { | ||||
|          vector signed short input = vec_ld(0, in); | ||||
|          vector signed int hi      = vec_unpackh(input); | ||||
|          vector signed int lo      = vec_unpackl(input); | ||||
|          vector float out_hi       = vec_madd(vec_ctf(hi, 15), gain_vec, zero_vec); | ||||
|          vector float out_lo       = vec_madd(vec_ctf(lo, 15), gain_vec, zero_vec); | ||||
| 
 | ||||
|          vec_st(out_hi,  0, out); | ||||
|          vec_st(out_lo, 16, out); | ||||
|       } | ||||
| 
 | ||||
|       samples_in -= i; | ||||
|    } | ||||
| 
 | ||||
|    samples = samples_in; | ||||
|    i       = 0; | ||||
| 
 | ||||
| #elif defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS) | ||||
|    if (s16_to_float_neon_enabled) | ||||
|    { | ||||
|       size_t aligned_samples = samples & ~7; | ||||
|       if (aligned_samples) | ||||
|          convert_s16_float_asm(out, in, aligned_samples, &gain); | ||||
| 
 | ||||
|       /* Could do all conversion in ASM, but keep it simple for now. */ | ||||
|       out     = out + aligned_samples; | ||||
|       in      = in  + aligned_samples; | ||||
|       samples = samples - aligned_samples; | ||||
|       i       = 0; | ||||
|    } | ||||
| 
 | ||||
| #elif defined(_MIPS_ARCH_ALLEGREX) | ||||
| #ifdef DEBUG | ||||
|    /* Make sure the buffer is 16 byte aligned, this should be the
 | ||||
|     * default behaviour of malloc in the PSPSDK. | ||||
|     * Only the output buffer can be assumed to be 16-byte aligned. */ | ||||
|    retro_assert(((uintptr_t)out & 0xf) == 0); | ||||
| #endif | ||||
| 
 | ||||
|    gain = gain / 0x8000; | ||||
|    __asm__ ( | ||||
|          ".set    push                    \n" | ||||
|          ".set    noreorder               \n" | ||||
|          "mtv     %0, s200                \n" | ||||
|          ".set    pop                     \n" | ||||
|          ::"r"(gain)); | ||||
| 
 | ||||
|    for (i = 0; i + 16 <= samples; i += 16) | ||||
|    { | ||||
|       __asm__ ( | ||||
|             ".set    push                 \n" | ||||
|             ".set    noreorder            \n" | ||||
| 
 | ||||
|             "lv.s    s100,  0(%0)         \n" | ||||
|             "lv.s    s101,  4(%0)         \n" | ||||
|             "lv.s    s110,  8(%0)         \n" | ||||
|             "lv.s    s111, 12(%0)         \n" | ||||
|             "lv.s    s120, 16(%0)         \n" | ||||
|             "lv.s    s121, 20(%0)         \n" | ||||
|             "lv.s    s130, 24(%0)         \n" | ||||
|             "lv.s    s131, 28(%0)         \n" | ||||
| 
 | ||||
|             "vs2i.p  c100, c100           \n" | ||||
|             "vs2i.p  c110, c110           \n" | ||||
|             "vs2i.p  c120, c120           \n" | ||||
|             "vs2i.p  c130, c130           \n" | ||||
| 
 | ||||
|             "vi2f.q  c100, c100, 16       \n" | ||||
|             "vi2f.q  c110, c110, 16       \n" | ||||
|             "vi2f.q  c120, c120, 16       \n" | ||||
|             "vi2f.q  c130, c130, 16       \n" | ||||
| 
 | ||||
|             "vmscl.q e100, e100, s200     \n" | ||||
| 
 | ||||
|             "sv.q    c100,  0(%1)         \n" | ||||
|             "sv.q    c110, 16(%1)         \n" | ||||
|             "sv.q    c120, 32(%1)         \n" | ||||
|             "sv.q    c130, 48(%1)         \n" | ||||
| 
 | ||||
|             ".set    pop                  \n" | ||||
|             :: "r"(in + i), "r"(out + i)); | ||||
|    } | ||||
| 
 | ||||
| #endif | ||||
|    gain    = gain / 0x8000; | ||||
| 
 | ||||
|    for (; i < samples; i++) | ||||
|       out[i] = (float)in[i] * gain; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * convert_s16_to_float_init_simd: | ||||
|  * | ||||
|  * Sets up function pointers for conversion | ||||
|  * functions based on CPU features. | ||||
|  **/ | ||||
| void convert_s16_to_float_init_simd(void) | ||||
| { | ||||
| #if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS) | ||||
|    unsigned cpu = cpu_features_get(); | ||||
| 
 | ||||
|    if (cpu & RETRO_SIMD_NEON) | ||||
|       s16_to_float_neon_enabled = true; | ||||
| #endif | ||||
| } | ||||
|  | @ -0,0 +1,76 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (s16_to_float_neon.S). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #if defined(__ARM_NEON__) | ||||
| 
 | ||||
| #ifndef __MACH__ | ||||
| .arm | ||||
| #endif | ||||
| 
 | ||||
| .align 4
 | ||||
| .globl convert_s16_float_asm
 | ||||
| #ifndef __MACH__ | ||||
| .type convert_s16_float_asm, %function | ||||
| #endif | ||||
| .globl _convert_s16_float_asm
 | ||||
| #ifndef __MACH__ | ||||
| .type _convert_s16_float_asm, %function | ||||
| #endif | ||||
| # convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain) | ||||
| convert_s16_float_asm: | ||||
| _convert_s16_float_asm: | ||||
|    # Hacky way to get a constant of 2^-15. | ||||
|    # Might be faster to just load a constant from memory. | ||||
|    # It's just done once however ... | ||||
|    vmov.f32 q8, #0.25 | ||||
|    vmul.f32 q8, q8, q8 | ||||
|    vmul.f32 q8, q8, q8 | ||||
|    vmul.f32 q8, q8, q8 | ||||
|    vadd.f32 q8, q8, q8 | ||||
| 
 | ||||
|    # Apply gain | ||||
|    vld1.f32 {d6[0]}, [r3] | ||||
|    vmul.f32 q8, q8, d6[0] | ||||
| 
 | ||||
| 1: | ||||
|    # Preload here? | ||||
|    vld1.s16 {q0}, [r1]! | ||||
| 
 | ||||
|    # Widen to 32-bit | ||||
|    vmovl.s16 q1, d0 | ||||
|    vmovl.s16 q2, d1 | ||||
| 
 | ||||
|    # Convert to float | ||||
|    vcvt.f32.s32 q1, q1 | ||||
|    vcvt.f32.s32 q2, q2 | ||||
| 
 | ||||
|    vmul.f32 q1, q1, q8 | ||||
|    vmul.f32 q2, q2, q8 | ||||
| 
 | ||||
|    vst1.f32 {q1-q2}, [r0]! | ||||
| 
 | ||||
|    # Guaranteed to get samples in multiples of 8. | ||||
|    subs r2, r2, #8 | ||||
|    bne 1b | ||||
| 
 | ||||
|    bx lr | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,71 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (s16_to_float_neon.S). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #if defined(__ARM_NEON__) | ||||
| 
 | ||||
| #if defined(__thumb__) | ||||
| #define DECL_ARMMODE(x) "  .align 2\n" "  .global " x "\n" "  .thumb\n" "  .thumb_func\n" "  .type " x ", %function\n" x ":\n" | ||||
| #else | ||||
| #define DECL_ARMMODE(x) "  .align 4\n" "  .global " x "\n" "  .arm\n" x ":\n" | ||||
| #endif | ||||
| 
 | ||||
| asm( | ||||
|     DECL_ARMMODE("convert_s16_float_asm") | ||||
|     DECL_ARMMODE("_convert_s16_float_asm") | ||||
|     "# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)\n" | ||||
|     "   # Hacky way to get a constant of 2^-15.\n" | ||||
|     "   # Might be faster to just load a constant from memory.\n" | ||||
|     "   # It's just done once however ...\n" | ||||
|     "   vmov.f32 q8, #0.25\n" | ||||
|     "   vmul.f32 q8, q8, q8\n" | ||||
|     "   vmul.f32 q8, q8, q8\n" | ||||
|     "   vmul.f32 q8, q8, q8\n" | ||||
|     "   vadd.f32 q8, q8, q8\n" | ||||
|     "\n" | ||||
|     "   # Apply gain\n" | ||||
|     "   vld1.f32 {d6[0]}, [r3]\n" | ||||
|     "   vmul.f32 q8, q8, d6[0]\n" | ||||
|     "\n" | ||||
|     "1:\n" | ||||
|     "   # Preload here?\n" | ||||
|     "   vld1.s16 {q0}, [r1]!\n" | ||||
|     "\n" | ||||
|     "   # Widen to 32-bit\n" | ||||
|     "   vmovl.s16 q1, d0\n" | ||||
|     "   vmovl.s16 q2, d1\n" | ||||
|     "\n" | ||||
|     "   # Convert to float\n" | ||||
|     "   vcvt.f32.s32 q1, q1\n" | ||||
|     "   vcvt.f32.s32 q2, q2\n" | ||||
|     "\n" | ||||
|     "   vmul.f32 q1, q1, q8\n" | ||||
|     "   vmul.f32 q2, q2, q8\n" | ||||
|     "\n" | ||||
|     "   vst1.f32 {q1-q2}, [r0]!\n" | ||||
|     "\n" | ||||
|     "   # Guaranteed to get samples in multiples of 8.\n" | ||||
|     "   subs r2, r2, #8\n" | ||||
|     "   bne 1b\n" | ||||
|     "\n" | ||||
|     "   bx lr\n" | ||||
|     "\n" | ||||
|     ); | ||||
| #endif | ||||
|  | @ -0,0 +1,325 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (dsp_filter.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| 
 | ||||
| #include <compat/posix_string.h> | ||||
| #include <dynamic/dylib.h> | ||||
| 
 | ||||
| #include <file/file_path.h> | ||||
| #include <file/config_file_userdata.h> | ||||
| #include <features/features_cpu.h> | ||||
| #include <lists/string_list.h> | ||||
| #include <string/stdstring.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| #include <audio/dsp_filter.h> | ||||
| 
 | ||||
| struct retro_dsp_plug | ||||
| { | ||||
| #ifdef HAVE_DYLIB | ||||
|    dylib_t lib; | ||||
| #endif | ||||
|    const struct dspfilter_implementation *impl; | ||||
| }; | ||||
| 
 | ||||
| struct retro_dsp_instance | ||||
| { | ||||
|    const struct dspfilter_implementation *impl; | ||||
|    void *impl_data; | ||||
| }; | ||||
| 
 | ||||
| struct retro_dsp_filter | ||||
| { | ||||
|    config_file_t *conf; | ||||
| 
 | ||||
|    struct retro_dsp_plug *plugs; | ||||
|    unsigned num_plugs; | ||||
| 
 | ||||
|    struct retro_dsp_instance *instances; | ||||
|    unsigned num_instances; | ||||
| }; | ||||
| 
 | ||||
| static const struct dspfilter_implementation *find_implementation( | ||||
|       retro_dsp_filter_t *dsp, const char *ident) | ||||
| { | ||||
|    unsigned i; | ||||
|    for (i = 0; i < dsp->num_plugs; i++) | ||||
|    { | ||||
|       if (string_is_equal(dsp->plugs[i].impl->short_ident, ident)) | ||||
|          return dsp->plugs[i].impl; | ||||
|    } | ||||
| 
 | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_config dspfilter_config = { | ||||
|    config_userdata_get_float, | ||||
|    config_userdata_get_int, | ||||
|    config_userdata_get_float_array, | ||||
|    config_userdata_get_int_array, | ||||
|    config_userdata_get_string, | ||||
|    config_userdata_free, | ||||
| }; | ||||
| 
 | ||||
| static bool create_filter_graph(retro_dsp_filter_t *dsp, float sample_rate) | ||||
| { | ||||
|    unsigned i; | ||||
|    struct retro_dsp_instance *instances = NULL; | ||||
|    unsigned filters                     = 0; | ||||
| 
 | ||||
|    if (!config_get_uint(dsp->conf, "filters", &filters)) | ||||
|       return false; | ||||
| 
 | ||||
|    instances = (struct retro_dsp_instance*)calloc(filters, sizeof(*instances)); | ||||
|    if (!instances) | ||||
|       return false; | ||||
| 
 | ||||
|    dsp->instances     = instances; | ||||
|    dsp->num_instances = filters; | ||||
| 
 | ||||
|    for (i = 0; i < filters; i++) | ||||
|    { | ||||
|       struct config_file_userdata userdata; | ||||
|       struct dspfilter_info info; | ||||
|       char key[64]; | ||||
|       char name[64]; | ||||
| 
 | ||||
|       key[0] = name[0] = '\0'; | ||||
| 
 | ||||
|       info.input_rate  = sample_rate; | ||||
| 
 | ||||
|       snprintf(key, sizeof(key), "filter%u", i); | ||||
| 
 | ||||
|       if (!config_get_array(dsp->conf, key, name, sizeof(name))) | ||||
|          return false; | ||||
| 
 | ||||
|       dsp->instances[i].impl = find_implementation(dsp, name); | ||||
|       if (!dsp->instances[i].impl) | ||||
|          return false; | ||||
| 
 | ||||
|       userdata.conf = dsp->conf; | ||||
|       /* Index-specific configs take priority over ident-specific. */ | ||||
|       userdata.prefix[0] = key; | ||||
|       userdata.prefix[1] = dsp->instances[i].impl->short_ident; | ||||
| 
 | ||||
|       dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info, | ||||
|             &dspfilter_config, &userdata); | ||||
|       if (!dsp->instances[i].impl_data) | ||||
|          return false; | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| #if defined(HAVE_FILTERS_BUILTIN) | ||||
| extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask); | ||||
| extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask); | ||||
| extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask); | ||||
| extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask); | ||||
| extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask); | ||||
| extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask); | ||||
| extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask); | ||||
| 
 | ||||
| static const dspfilter_get_implementation_t dsp_plugs_builtin[] = { | ||||
|    panning_dspfilter_get_implementation, | ||||
|    iir_dspfilter_get_implementation, | ||||
|    echo_dspfilter_get_implementation, | ||||
|    phaser_dspfilter_get_implementation, | ||||
|    wahwah_dspfilter_get_implementation, | ||||
|    eq_dspfilter_get_implementation, | ||||
|    chorus_dspfilter_get_implementation, | ||||
| }; | ||||
| 
 | ||||
| static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list) | ||||
| { | ||||
|    unsigned i; | ||||
|    dspfilter_simd_mask_t mask   = (dspfilter_simd_mask_t)cpu_features_get(); | ||||
|    struct retro_dsp_plug *plugs = (struct retro_dsp_plug*) | ||||
|       calloc(ARRAY_SIZE(dsp_plugs_builtin), sizeof(*plugs)); | ||||
| 
 | ||||
|    if (!plugs) | ||||
|       return false; | ||||
| 
 | ||||
|    dsp->plugs     = plugs; | ||||
|    dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin); | ||||
| 
 | ||||
|    for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++) | ||||
|    { | ||||
|       dsp->plugs[i].impl = dsp_plugs_builtin[i](mask); | ||||
|       if (!dsp->plugs[i].impl) | ||||
|          return false; | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| #elif defined(HAVE_DYLIB) | ||||
| static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list) | ||||
| { | ||||
|    unsigned i; | ||||
|    dspfilter_simd_mask_t mask = cpu_features_get(); | ||||
|    unsigned list_size         = list ? list->size : 0; | ||||
| 
 | ||||
|    for (i = 0; i < list_size; i++) | ||||
|    { | ||||
|       dspfilter_get_implementation_t cb; | ||||
|       const struct dspfilter_implementation *impl = NULL; | ||||
|       struct retro_dsp_plug *new_plugs            = NULL; | ||||
|       dylib_t lib                                 = | ||||
|          dylib_load(list->elems[i].data); | ||||
| 
 | ||||
|       if (!lib) | ||||
|          continue; | ||||
| 
 | ||||
|       cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation"); | ||||
|       if (!cb) | ||||
|       { | ||||
|          dylib_close(lib); | ||||
|          continue; | ||||
|       } | ||||
| 
 | ||||
|       impl = cb(mask); | ||||
|       if (!impl) | ||||
|       { | ||||
|          dylib_close(lib); | ||||
|          continue; | ||||
|       } | ||||
| 
 | ||||
|       if (impl->api_version != DSPFILTER_API_VERSION) | ||||
|       { | ||||
|          dylib_close(lib); | ||||
|          continue; | ||||
|       } | ||||
| 
 | ||||
|       new_plugs = (struct retro_dsp_plug*) | ||||
|          realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1)); | ||||
|       if (!new_plugs) | ||||
|       { | ||||
|          dylib_close(lib); | ||||
|          return false; | ||||
|       } | ||||
| 
 | ||||
|       /* Found plug. */ | ||||
| 
 | ||||
|       dsp->plugs = new_plugs; | ||||
|       dsp->plugs[dsp->num_plugs].lib = lib; | ||||
|       dsp->plugs[dsp->num_plugs].impl = impl; | ||||
|       dsp->num_plugs++; | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| retro_dsp_filter_t *retro_dsp_filter_new( | ||||
|       const char *filter_config, | ||||
|       void *string_data, | ||||
|       float sample_rate) | ||||
| { | ||||
|    config_file_t *conf           = NULL; | ||||
|    struct string_list *plugs     = NULL; | ||||
|    retro_dsp_filter_t *dsp       = (retro_dsp_filter_t*)calloc(1, sizeof(*dsp)); | ||||
| 
 | ||||
|    if (!dsp) | ||||
|       return NULL; | ||||
| 
 | ||||
|    conf = config_file_new(filter_config); | ||||
|    if (!conf)   /* Did not find config. */ | ||||
|       goto error; | ||||
| 
 | ||||
|    dsp->conf = conf; | ||||
| 
 | ||||
|    if (string_data) | ||||
|       plugs = (struct string_list*)string_data; | ||||
| 
 | ||||
| #if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN) | ||||
|    if (!append_plugs(dsp, plugs)) | ||||
|       goto error; | ||||
| #endif | ||||
| 
 | ||||
|    if (plugs) | ||||
|       string_list_free(plugs); | ||||
|    plugs = NULL; | ||||
| 
 | ||||
|    if (!create_filter_graph(dsp, sample_rate)) | ||||
|       goto error; | ||||
| 
 | ||||
|    return dsp; | ||||
| 
 | ||||
| error: | ||||
|    if (plugs) | ||||
|       string_list_free(plugs); | ||||
|    retro_dsp_filter_free(dsp); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| void retro_dsp_filter_free(retro_dsp_filter_t *dsp) | ||||
| { | ||||
|    unsigned i; | ||||
|    if (!dsp) | ||||
|       return; | ||||
| 
 | ||||
|    for (i = 0; i < dsp->num_instances; i++) | ||||
|    { | ||||
|       if (dsp->instances[i].impl_data && dsp->instances[i].impl) | ||||
|          dsp->instances[i].impl->free(dsp->instances[i].impl_data); | ||||
|    } | ||||
|    free(dsp->instances); | ||||
| 
 | ||||
| #ifdef HAVE_DYLIB | ||||
|    for (i = 0; i < dsp->num_plugs; i++) | ||||
|    { | ||||
|       if (dsp->plugs[i].lib) | ||||
|          dylib_close(dsp->plugs[i].lib); | ||||
|    } | ||||
|    free(dsp->plugs); | ||||
| #endif | ||||
| 
 | ||||
|    if (dsp->conf) | ||||
|       config_file_free(dsp->conf); | ||||
| 
 | ||||
|    free(dsp); | ||||
| } | ||||
| 
 | ||||
| void retro_dsp_filter_process(retro_dsp_filter_t *dsp, | ||||
|       struct retro_dsp_data *data) | ||||
| { | ||||
|    unsigned i; | ||||
|    struct dspfilter_output output = {0}; | ||||
|    struct dspfilter_input input   = {0}; | ||||
| 
 | ||||
|    output.samples = data->input; | ||||
|    output.frames  = data->input_frames; | ||||
| 
 | ||||
|    for (i = 0; i < dsp->num_instances; i++) | ||||
|    { | ||||
|       input.samples = output.samples; | ||||
|       input.frames  = output.frames; | ||||
|       dsp->instances[i].impl->process( | ||||
|             dsp->instances[i].impl_data, &output, &input); | ||||
|    } | ||||
| 
 | ||||
|    data->output        = output.samples; | ||||
|    data->output_frames = output.frames; | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| filters = 2 | ||||
| filter0 = iir | ||||
| filter1 = panning | ||||
| 
 | ||||
| iir_gain = 10.0 | ||||
| iir_type = BBOOST | ||||
| iir_frequency = 200.0 | ||||
| 
 | ||||
| # Avoids clipping. | ||||
| panning_left_mix = "0.3 0.0" | ||||
| panning_right_mix = "0.0 0.3" | ||||
| 
 | ||||
|  | @ -0,0 +1,22 @@ | |||
| filters = 4 | ||||
| filter0 = eq | ||||
| filter1 = reverb | ||||
| filter2 = iir | ||||
| filter3 = panning | ||||
| 
 | ||||
| eq_frequencies = "32 64 125 250 500 1000 2000 4000 8000 16000 20000" | ||||
| eq_gains = "6 9 12 7 6 5 7 9 11 6 0" | ||||
| 
 | ||||
| # Reverb - slight reverb | ||||
|  reverb_drytime = 0.5 | ||||
|  reverb_wettime = 0.15 | ||||
|  reverb_damping = 0.8 | ||||
|  reverb_roomwidth = 0.25 | ||||
|  reverb_roomsize = 0.25 | ||||
|   | ||||
| # IIR - filters out some harsh sounds on the upper end | ||||
| iir_type = RIAA_CD | ||||
| 
 | ||||
| # Panning - cut the volume a bit | ||||
| panning_left_mix = "0.75 0.0" | ||||
| panning_right_mix = "0.0 0.75" | ||||
|  | @ -0,0 +1,14 @@ | |||
| filters = 1 | ||||
| filter0 = chorus | ||||
| 
 | ||||
| # Controls the base delay of the chorus (milliseconds). | ||||
| # chorus_delay_ms = 25.0 | ||||
| # | ||||
| # Controls the depth of the delay. The delay will vary between delay_ms +/- depth_ms. | ||||
| # chorus_depth_ms = 1.0 | ||||
| # | ||||
| # Frequency of LFO which controls delay. | ||||
| # chorus_lfo_freq = 0.5 | ||||
| # | ||||
| # Controls dry/wet-ness of effect. 1.0 = full chorus, 0.0 = no chorus. | ||||
| # chorus_drywet = 0.8 | ||||
|  | @ -0,0 +1,4 @@ | |||
| filters = 1 | ||||
| filter0 = crystalizer | ||||
| # Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max. | ||||
| crystalizer_intensity = 5.0 | ||||
|  | @ -0,0 +1,41 @@ | |||
| filters = 1 | ||||
| filter0 = eq | ||||
| 
 | ||||
| # Defaults | ||||
| 
 | ||||
| # Beta factor for Kaiser window. | ||||
| # Lower values will allow better frequency resolution, but more ripple. | ||||
| # eq_window_beta = 4.0 | ||||
| 
 | ||||
| # The block size on which FFT is done. | ||||
| # Too high value requires more processing as well as longer latency but | ||||
| # allows finer-grained control over the spectrum. | ||||
| # eq_block_size_log2 = 8 | ||||
| 
 | ||||
| # An array of which frequencies to control. | ||||
| # You can create an arbitrary amount of these sampling points. | ||||
| # The EQ will try to create a frequency response which fits well to these points. | ||||
| # The filter response is linearly interpolated between sampling points here. | ||||
| # | ||||
| # It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against. | ||||
| # If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB. | ||||
| # | ||||
| # E.g.: A boost of 3 dB at 1 kHz can be expressed as. | ||||
| # eq_frequencies = "500 1000 2000" | ||||
| # eq_gains = "0 3 0" | ||||
| # Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz. | ||||
| 
 | ||||
| # By default, this filter has a flat frequency response. | ||||
| 
 | ||||
| # Dumps the impulse response generated by the EQ as a plain-text file | ||||
| # with one coefficient per line. | ||||
| # eq_impulse_response_output = "eq_impulse.txt" | ||||
| # | ||||
| # Using GNU Octave or Matlab, you can plot the response with: | ||||
| # | ||||
| # f = fopen('/path/to/eq_impulse.txt'); | ||||
| # l = textscan(f, '%f'); | ||||
| # res = l{1}; | ||||
| # freqz(res, 1, 4096, 48000); | ||||
| # | ||||
| # It will give the response in Hz; 48000 is the default Output Rate of RetroArch | ||||
|  | @ -0,0 +1,19 @@ | |||
| filters = 1 | ||||
| filter0 = echo | ||||
| 
 | ||||
| # Somewhat fancy Echo filter. Can take any number of echo channels with varying delays (ms) and feedback factors. | ||||
| # Echo output from all channels can be fed back into each other to create a somewhat reverb-like effect if desired. | ||||
| 
 | ||||
| # Defaults, 200 ms delay echo with feedback: | ||||
| # Delay in ms. Takes an array with multiple channels. | ||||
| # echo_delay = "200" | ||||
| # Feedback factor for echo. | ||||
| # echo_feedback = "0.5" | ||||
| # Overall echo amplification. If too high, the echo becomes unstable due to feedback. | ||||
| # echo_amp = "0.2" | ||||
| 
 | ||||
| # Reverby preset. | ||||
| # echo_delay    = " 60  80 120 172 200 320 380" | ||||
| # echo_feedback = "0.5 0.5 0.4 0.3 0.5 0.3 0.2" | ||||
| 
 | ||||
| # echo_amp = "0.12" | ||||
|  | @ -0,0 +1,12 @@ | |||
| filters = 2 | ||||
| filter0 = echo | ||||
| filter1 = reverb | ||||
| 
 | ||||
| echo_delay = "200" | ||||
| echo_feedback = "0.6" | ||||
| echo_amp = "0.25" | ||||
| 
 | ||||
| reverb_roomwidth = 0.75 | ||||
| reverb_roomsize = 0.75 | ||||
| reverb_damping = 1.0 | ||||
| reverb_wettime = 0.3 | ||||
|  | @ -0,0 +1,7 @@ | |||
| filters = 1 | ||||
| filter0 = iir | ||||
| 
 | ||||
| iir_gain = -12.0 | ||||
| iir_type = HSH | ||||
| iir_frequency = 8000.0 | ||||
| 
 | ||||
|  | @ -0,0 +1,23 @@ | |||
| filters = 1 | ||||
| filter0 = iir | ||||
| 
 | ||||
| # Defaults. | ||||
| #iir_frequency = 1024.0 | ||||
| #iir_quality = 0.707 | ||||
| #iir_gain = 0.0 | ||||
| #iir_type = LPF | ||||
| 
 | ||||
| # Filter types: | ||||
| # LPF: Low-pass | ||||
| # HPF: High-pass | ||||
| # BPCSGF: Band-pass #1 | ||||
| # BPZPGF: Band-pass #2 | ||||
| # APF: Allpass | ||||
| # NOTCH: Notch filter | ||||
| # RIAA_phono: RIAA record/tape deemphasis | ||||
| # PEQ: peaking band EQ | ||||
| # BBOOST: Bassboost | ||||
| # LSH: Low-shelf | ||||
| # HSH: High-shelf | ||||
| # RIAA_CD: CD de-emphasis | ||||
| 
 | ||||
|  | @ -0,0 +1,47 @@ | |||
| filters = 1 | ||||
| filter0 = eq | ||||
| 
 | ||||
| eq_frequencies = "8000 10000 12500 16000 20000" | ||||
| eq_gains = "0 -30 -30 -30 -30" | ||||
| 
 | ||||
| # Low pass filter for the QSound chip from CPS-1/2. | ||||
| # Some games have aliasing due low quality samples, so you can hear some annoying noisy near 11 kHz | ||||
| 
 | ||||
| # Defaults | ||||
| 
 | ||||
| # Beta factor for Kaiser window. | ||||
| # Lower values will allow better frequency resolution, but more ripple. | ||||
| # eq_window_beta = 4.0 | ||||
| 
 | ||||
| # The block size on which FFT is done. | ||||
| # Too high value requires more processing as well as longer latency but | ||||
| # allows finer-grained control over the spectrum. | ||||
| # eq_block_size_log2 = 8 | ||||
| 
 | ||||
| # An array of which frequencies to control. | ||||
| # You can create an arbitrary amount of these sampling points. | ||||
| # The EQ will try to create a frequency response which fits well to these points. | ||||
| # The filter response is linearly interpolated between sampling points here. | ||||
| # | ||||
| # It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against. | ||||
| # If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB. | ||||
| # | ||||
| # E.g.: A boost of 3 dB at 1 kHz can be expressed as. | ||||
| # eq_frequencies = "500 1000 2000" | ||||
| # eq_gains = "0 3 0" | ||||
| # Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz. | ||||
| 
 | ||||
| # By default, this filter has a low pass response with cuttof frequency at ~8600 Hz. | ||||
| 
 | ||||
| # Dumps the impulse response generated by the EQ as a plain-text file | ||||
| # with one coefficient per line. | ||||
| # eq_impulse_response_output = "eq_impulse.txt" | ||||
| # | ||||
| # Using GNU Octave or Matlab, you can plot the response with: | ||||
| # | ||||
| # f = fopen('/path/to/eq_impulse.txt'); | ||||
| # l = textscan(f, '%f'); | ||||
| # res = l{1}; | ||||
| # freqz(res, 1, 4096, 48000); | ||||
| # | ||||
| # It will give the response in Hz; 48000 is the default Output Rate of RetroArch | ||||
|  | @ -0,0 +1,100 @@ | |||
| compiler    := gcc | ||||
| extra_flags := | ||||
| use_neon    := 0 | ||||
| build       = release | ||||
| DYLIB	      := so | ||||
| PREFIX      := /usr | ||||
| INSTALLDIR  := $(PREFIX)/lib/retroarch/filters/audio | ||||
| 
 | ||||
| ifeq ($(platform),) | ||||
|    platform = unix | ||||
|    ifeq ($(shell uname -a),) | ||||
|       platform = win | ||||
|    else ifneq ($(findstring MINGW,$(shell uname -a)),) | ||||
|       platform = win | ||||
|    else ifneq ($(findstring Darwin,$(shell uname -a)),) | ||||
|       platform = osx | ||||
|       arch = intel | ||||
|       ifeq ($(shell uname -p),powerpc) | ||||
|          arch = ppc | ||||
|       endif | ||||
|    else ifneq ($(findstring win,$(shell uname -a)),) | ||||
|       platform = win | ||||
|    endif | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(platform),gcc) | ||||
|    extra_rules_gcc := $(shell $(compiler) -dumpmachine) | ||||
| endif | ||||
| 
 | ||||
| ifneq (,$(findstring armv7,$(extra_rules_gcc))) | ||||
|    extra_flags += -mcpu=cortex-a9 -mtune=cortex-a9 -mfpu=neon | ||||
|    use_neon := 1 | ||||
| endif | ||||
| 
 | ||||
| ifneq (,$(findstring hardfloat,$(extra_rules_gcc))) | ||||
|    extra_flags += -mfloat-abi=hard | ||||
| endif | ||||
| 
 | ||||
| ifeq (release,$(build)) | ||||
|    extra_flags += -O2 | ||||
| endif | ||||
| 
 | ||||
| ifeq (debug,$(build)) | ||||
|    extra_flags += -O0 -g | ||||
| endif | ||||
| 
 | ||||
| ldflags := $(LDFLAGS) -shared -lm -Wl,--version-script=link.T | ||||
| 
 | ||||
| ifeq ($(platform), unix) | ||||
|    DYLIB = so | ||||
| else ifeq ($(platform), osx) | ||||
|    compiler := $(CC) | ||||
|    DYLIB = dylib | ||||
|    ldflags := -dynamiclib | ||||
| else | ||||
|    extra_flags += -static-libgcc -static-libstdc++ | ||||
|    DYLIB = dll | ||||
| endif | ||||
| 
 | ||||
| CC      := $(compiler) -Wall | ||||
| CXX     := $(subst CC,++,$(compiler)) -std=gnu++0x -Wall | ||||
| flags   := $(CPPFLAGS) $(CFLAGS) -fPIC $(extra_flags) -I../../include | ||||
| asflags := $(ASFLAGS) -fPIC  $(extra_flags) | ||||
| objects := | ||||
| 
 | ||||
| ifeq (1,$(use_neon)) | ||||
|    ASMFLAGS := -INEON/asm  | ||||
|    asflags += -mfpu=neon | ||||
| endif | ||||
| 
 | ||||
| plugs := $(wildcard *.c) | ||||
| objects := $(plugs:.c=.o) | ||||
| targets := $(objects:.o=.$(DYLIB)) | ||||
| 
 | ||||
| all: build; | ||||
| 
 | ||||
| %.o: %.S | ||||
| 	$(CC) -c -o $@ $(asflags)  $(ASMFLAGS)  $< | ||||
| 
 | ||||
| %.o: %.c | ||||
| 	$(CC) -c -o $@ $(flags) $< | ||||
| 
 | ||||
| %.$(DYLIB): %.o | ||||
| 	$(CC) -o $@ $(ldflags) $(flags) $^ | ||||
| 
 | ||||
| build: $(targets) | ||||
| 
 | ||||
| clean: | ||||
| 	rm -f *.o | ||||
| 	rm -f *.$(DYLIB) | ||||
| 
 | ||||
| strip: | ||||
| 	strip -s *.$(DYLIB) | ||||
| 
 | ||||
| install: | ||||
| 	mkdir -p $(DESTDIR)$(INSTALLDIR) | ||||
| 	cp -t $(DESTDIR)$(INSTALLDIR) $(targets) *.dsp | ||||
| 
 | ||||
| test-install: | ||||
| 	DESTDIR=/tmp/build $(MAKE) install | ||||
|  | @ -0,0 +1,12 @@ | |||
| filters = 1 | ||||
| filter0 = panning | ||||
| 
 | ||||
| # Gains are linear. | ||||
| 
 | ||||
| # Stereo Mono: | ||||
|  panning_left_mix = "0.5 0.5" | ||||
|  panning_right_mix = "0.5 0.5" | ||||
| 
 | ||||
| # Mono on one speaker: | ||||
| # panning_left_mix = "0.5 0.5" | ||||
| # panning_right_mix = "0.0 0.0" | ||||
|  | @ -0,0 +1,23 @@ | |||
| filters = 1 | ||||
| filter0 = panning | ||||
| 
 | ||||
| # Gains are linear. | ||||
| 
 | ||||
| # The default. Left and right channels map to each other. | ||||
| panning_left_mix = "1.0 0.0" | ||||
| panning_right_mix = "0.0 1.0" | ||||
| 
 | ||||
| # Some examples: | ||||
| # | ||||
| # Mono: | ||||
| # panning_left_mix = "0.5 0.5" | ||||
| # panning_right_mix = "0.5 0.5" | ||||
| 
 | ||||
| # Swap left and right channels: | ||||
| # panning_left_mix = "0.0 1.0" | ||||
| # panning_right_mix = "1.0 0.0" | ||||
| # | ||||
| # Mono on one speaker: | ||||
| # panning_left_mix = "0.5 0.5" | ||||
| # panning_right_mix = "0.0 0.0" | ||||
| 
 | ||||
|  | @ -0,0 +1,11 @@ | |||
| filters = 1 | ||||
| filter0 = phaser | ||||
| 
 | ||||
| # Defaults. | ||||
| # phaser_lfo_freq = 0.4 | ||||
| # phaser_lfo_start_phase = 0.0 | ||||
| # phaser_feedback = 0.0 | ||||
| # phaser_depth = 0.4 | ||||
| # phaser_dry_wet = 0.5 | ||||
| # phaser_stages = 2 | ||||
| 
 | ||||
|  | @ -0,0 +1,10 @@ | |||
| filters = 1 | ||||
| filter0 = reverb | ||||
| 
 | ||||
| # Defaults. | ||||
| # reverb_drytime = 0.43 | ||||
| # reverb_wettime = 0.4 | ||||
| # reverb_damping = 0.8 | ||||
| # reverb_roomwidth = 0.56 | ||||
| # reverb_roomsize = 0.56 | ||||
| 
 | ||||
|  | @ -0,0 +1,10 @@ | |||
| filters = 1 | ||||
| filter0 = wahwah | ||||
| 
 | ||||
| # Defaults. | ||||
| # wahwah_lfo_freq = 1.5 | ||||
| # wahwah_lfo_start_phase = 0.0 | ||||
| # wahwah_freq_offset = 0.3 | ||||
| # wahwah_depth = 0.7 | ||||
| # wahwah_resonance = 2.5 | ||||
| 
 | ||||
|  | @ -0,0 +1,161 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (chorus.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| #define CHORUS_MAX_DELAY 4096 | ||||
| #define CHORUS_DELAY_MASK (CHORUS_MAX_DELAY - 1) | ||||
| 
 | ||||
| struct chorus_data | ||||
| { | ||||
|    float old[2][CHORUS_MAX_DELAY]; | ||||
|    unsigned old_ptr; | ||||
| 
 | ||||
|    float delay; | ||||
|    float depth; | ||||
|    float input_rate; | ||||
|    float mix_dry; | ||||
|    float mix_wet; | ||||
|    unsigned lfo_ptr; | ||||
|    unsigned lfo_period; | ||||
| }; | ||||
| 
 | ||||
| static void chorus_free(void *data) | ||||
| { | ||||
|    if (data) | ||||
|       free(data); | ||||
| } | ||||
| 
 | ||||
| static void chorus_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    unsigned i; | ||||
|    float *out             = NULL; | ||||
|    struct chorus_data *ch = (struct chorus_data*)data; | ||||
| 
 | ||||
|    output->samples        = input->samples; | ||||
|    output->frames         = input->frames; | ||||
|    out                    = output->samples; | ||||
| 
 | ||||
|    for (i = 0; i < input->frames; i++, out += 2) | ||||
|    { | ||||
|       unsigned delay_int; | ||||
|       float delay_frac, l_a, l_b, r_a, r_b; | ||||
|       float chorus_l, chorus_r; | ||||
|       float in[2] = { out[0], out[1] }; | ||||
|       float delay = ch->delay + ch->depth * sin((2.0 * M_PI * ch->lfo_ptr++) / ch->lfo_period); | ||||
| 
 | ||||
|       delay *= ch->input_rate; | ||||
|       if (ch->lfo_ptr >= ch->lfo_period) | ||||
|          ch->lfo_ptr = 0; | ||||
| 
 | ||||
|       delay_int = (unsigned)delay; | ||||
| 
 | ||||
|       if (delay_int >= CHORUS_MAX_DELAY - 1) | ||||
|          delay_int = CHORUS_MAX_DELAY - 2; | ||||
| 
 | ||||
|       delay_frac = delay - delay_int; | ||||
| 
 | ||||
|       ch->old[0][ch->old_ptr] = in[0]; | ||||
|       ch->old[1][ch->old_ptr] = in[1]; | ||||
| 
 | ||||
|       l_a         = ch->old[0][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK]; | ||||
|       l_b         = ch->old[0][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK]; | ||||
|       r_a         = ch->old[1][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK]; | ||||
|       r_b         = ch->old[1][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK]; | ||||
| 
 | ||||
|       /* Lerp introduces aliasing of the chorus component,
 | ||||
|        * but doing full polyphase here is probably overkill. */ | ||||
|       chorus_l    = l_a * (1.0f - delay_frac) + l_b * delay_frac; | ||||
|       chorus_r    = r_a * (1.0f - delay_frac) + r_b * delay_frac; | ||||
| 
 | ||||
|       out[0]      = ch->mix_dry * in[0] + ch->mix_wet * chorus_l; | ||||
|       out[1]      = ch->mix_dry * in[1] + ch->mix_wet * chorus_r; | ||||
| 
 | ||||
|       ch->old_ptr = (ch->old_ptr + 1) & CHORUS_DELAY_MASK; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *chorus_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    float delay, depth, lfo_freq, drywet; | ||||
|    struct chorus_data *ch = (struct chorus_data*)calloc(1, sizeof(*ch)); | ||||
|    if (!ch) | ||||
|       return NULL; | ||||
| 
 | ||||
|    config->get_float(userdata, "delay_ms", &delay, 25.0f); | ||||
|    config->get_float(userdata, "depth_ms", &depth, 1.0f); | ||||
|    config->get_float(userdata, "lfo_freq", &lfo_freq, 0.5f); | ||||
|    config->get_float(userdata, "drywet", &drywet, 0.8f); | ||||
| 
 | ||||
|    delay /= 1000.0f; | ||||
|    depth /= 1000.0f; | ||||
| 
 | ||||
|    if (depth > delay) | ||||
|       depth = delay; | ||||
| 
 | ||||
|    if (drywet < 0.0f) | ||||
|       drywet = 0.0f; | ||||
|    else if (drywet > 1.0f) | ||||
|       drywet = 1.0f; | ||||
| 
 | ||||
|    ch->mix_dry = 1.0f - 0.5f * drywet; | ||||
|    ch->mix_wet = 0.5f * drywet; | ||||
| 
 | ||||
|    ch->delay = delay; | ||||
|    ch->depth = depth; | ||||
|    ch->lfo_period = (1.0f / lfo_freq) * info->input_rate; | ||||
|    ch->input_rate = info->input_rate; | ||||
|    if (!ch->lfo_period) | ||||
|       ch->lfo_period = 1; | ||||
|    return ch; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_implementation chorus_plug = { | ||||
|    chorus_init, | ||||
|    chorus_process, | ||||
|    chorus_free, | ||||
| 
 | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "Chorus", | ||||
|    "chorus", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation chorus_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation * | ||||
| dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &chorus_plug; | ||||
| } | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
|  | @ -0,0 +1,93 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (echo.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| struct delta_data | ||||
| { | ||||
|    float intensity; | ||||
|    float old[2]; | ||||
| }; | ||||
| 
 | ||||
| static void delta_free(void *data) | ||||
| { | ||||
|    free(data); | ||||
| } | ||||
| 
 | ||||
| static void delta_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    unsigned i, c; | ||||
|    struct delta_data *d = (struct delta_data*)data; | ||||
|    float *out             = output->samples; | ||||
|    output->samples        = input->samples; | ||||
|    output->frames         = input->frames; | ||||
| 
 | ||||
|    for (i = 0; i < input->frames; i++) | ||||
|    { | ||||
|       for (c = 0; c < 2; c++) | ||||
|       { | ||||
|            float current = *out; | ||||
|            *out++ = current + (current - d->old[c]) * d->intensity; | ||||
|            d->old[c] = current; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *delta_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    struct delta_data *d = (struct delta_data*)calloc(1, sizeof(*d)); | ||||
|    if (!d) | ||||
|       return NULL; | ||||
|    config->get_float(userdata, "intensity", &d->intensity, 5.0f); | ||||
|    return d; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_implementation delta_plug = { | ||||
|    delta_init, | ||||
|    delta_process, | ||||
|    delta_free, | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "Delta Sharpening", | ||||
|    "crystalizer", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation delta_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &delta_plug; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
|  | @ -0,0 +1,182 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (echo.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| struct echo_channel | ||||
| { | ||||
|    float *buffer; | ||||
|    unsigned ptr; | ||||
|    unsigned frames; | ||||
|    float feedback; | ||||
| }; | ||||
| 
 | ||||
| struct echo_data | ||||
| { | ||||
|    struct echo_channel *channels; | ||||
|    unsigned num_channels; | ||||
|    float amp; | ||||
| }; | ||||
| 
 | ||||
| static void echo_free(void *data) | ||||
| { | ||||
|    unsigned i; | ||||
|    struct echo_data *echo = (struct echo_data*)data; | ||||
| 
 | ||||
|    for (i = 0; i < echo->num_channels; i++) | ||||
|       free(echo->channels[i].buffer); | ||||
|    free(echo->channels); | ||||
|    free(echo); | ||||
| } | ||||
| 
 | ||||
| static void echo_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    unsigned i, c; | ||||
|    float *out             = NULL; | ||||
|    struct echo_data *echo = (struct echo_data*)data; | ||||
| 
 | ||||
|    output->samples        = input->samples; | ||||
|    output->frames         = input->frames; | ||||
| 
 | ||||
|    out                    = output->samples; | ||||
| 
 | ||||
|    for (i = 0; i < input->frames; i++, out += 2) | ||||
|    { | ||||
|       float left, right; | ||||
|       float echo_left  = 0.0f; | ||||
|       float echo_right = 0.0f; | ||||
| 
 | ||||
|       for (c = 0; c < echo->num_channels; c++) | ||||
|       { | ||||
|          echo_left  += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0]; | ||||
|          echo_right += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1]; | ||||
|       } | ||||
| 
 | ||||
|       echo_left  *= echo->amp; | ||||
|       echo_right *= echo->amp; | ||||
| 
 | ||||
|       left        = out[0] + echo_left; | ||||
|       right       = out[1] + echo_right; | ||||
| 
 | ||||
|       for (c = 0; c < echo->num_channels; c++) | ||||
|       { | ||||
|          float feedback_left  = out[0] + echo->channels[c].feedback * echo_left; | ||||
|          float feedback_right = out[1] + echo->channels[c].feedback * echo_right; | ||||
| 
 | ||||
|          echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0] = feedback_left; | ||||
|          echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1] = feedback_right; | ||||
| 
 | ||||
|          echo->channels[c].ptr = (echo->channels[c].ptr + 1) % echo->channels[c].frames; | ||||
|       } | ||||
| 
 | ||||
|       out[0] = left; | ||||
|       out[1] = right; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *echo_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    unsigned i, channels; | ||||
|    struct echo_channel *echo_channels    = NULL; | ||||
|    float *delay                          = NULL; | ||||
|    float *feedback                       = NULL; | ||||
|    unsigned num_delay                    = 0; | ||||
|    unsigned num_feedback                 = 0; | ||||
| 
 | ||||
|    static const float default_delay[]    = { 200.0f }; | ||||
|    static const float default_feedback[] = { 0.5f }; | ||||
|    struct echo_data *echo                = (struct echo_data*) | ||||
|       calloc(1, sizeof(*echo)); | ||||
| 
 | ||||
|    if (!echo) | ||||
|       return NULL; | ||||
| 
 | ||||
|    config->get_float_array(userdata, "delay", &delay, | ||||
|          &num_delay, default_delay, 1); | ||||
|    config->get_float_array(userdata, "feedback", &feedback, | ||||
|          &num_feedback, default_feedback, 1); | ||||
|    config->get_float(userdata, "amp", &echo->amp, 0.2f); | ||||
| 
 | ||||
|    channels       = num_feedback = num_delay = MIN(num_delay, num_feedback); | ||||
| 
 | ||||
|    echo_channels = (struct echo_channel*)calloc(channels, | ||||
|          sizeof(*echo_channels)); | ||||
| 
 | ||||
|    if (!echo_channels) | ||||
|       goto error; | ||||
| 
 | ||||
|    echo->channels     = echo_channels; | ||||
|    echo->num_channels = channels; | ||||
| 
 | ||||
|    for (i = 0; i < channels; i++) | ||||
|    { | ||||
|       unsigned frames = (unsigned)(delay[i] * info->input_rate / 1000.0f + 0.5f); | ||||
|       if (!frames) | ||||
|          goto error; | ||||
| 
 | ||||
|       echo->channels[i].buffer = (float*)calloc(frames, 2 * sizeof(float)); | ||||
|       if (!echo->channels[i].buffer) | ||||
|          goto error; | ||||
| 
 | ||||
|       echo->channels[i].frames = frames; | ||||
|       echo->channels[i].feedback = feedback[i]; | ||||
|    } | ||||
| 
 | ||||
|    config->free(delay); | ||||
|    config->free(feedback); | ||||
|    return echo; | ||||
| 
 | ||||
| error: | ||||
|    config->free(delay); | ||||
|    config->free(feedback); | ||||
|    echo_free(echo); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static const struct dspfilter_implementation echo_plug = { | ||||
|    echo_init, | ||||
|    echo_process, | ||||
|    echo_free, | ||||
| 
 | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "Multi-Echo", | ||||
|    "echo", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation echo_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &echo_plug; | ||||
| } | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
|  | @ -0,0 +1,352 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (eq.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <filters.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| #include "fft/fft.c" | ||||
| 
 | ||||
| struct eq_data | ||||
| { | ||||
|    fft_t *fft; | ||||
|    float buffer[8 * 1024]; | ||||
| 
 | ||||
|    float *save; | ||||
|    float *block; | ||||
|    fft_complex_t *filter; | ||||
|    fft_complex_t *fftblock; | ||||
|    unsigned block_size; | ||||
|    unsigned block_ptr; | ||||
| }; | ||||
| 
 | ||||
| struct eq_gain | ||||
| { | ||||
|    float freq; | ||||
|    float gain; /* Linear. */ | ||||
| }; | ||||
| 
 | ||||
| static void eq_free(void *data) | ||||
| { | ||||
|    struct eq_data *eq = (struct eq_data*)data; | ||||
|    if (!eq) | ||||
|       return; | ||||
| 
 | ||||
|    fft_free(eq->fft); | ||||
|    free(eq->save); | ||||
|    free(eq->block); | ||||
|    free(eq->fftblock); | ||||
|    free(eq->filter); | ||||
|    free(eq); | ||||
| } | ||||
| 
 | ||||
| static void eq_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    float *out; | ||||
|    const float *in; | ||||
|    unsigned input_frames; | ||||
|    struct eq_data *eq = (struct eq_data*)data; | ||||
| 
 | ||||
|    output->samples    = eq->buffer; | ||||
|    output->frames     = 0; | ||||
| 
 | ||||
|    out                = eq->buffer; | ||||
|    in                 = input->samples; | ||||
|    input_frames       = input->frames; | ||||
| 
 | ||||
|    while (input_frames) | ||||
|    { | ||||
|       unsigned write_avail = eq->block_size - eq->block_ptr; | ||||
| 
 | ||||
|       if (input_frames < write_avail) | ||||
|          write_avail = input_frames; | ||||
| 
 | ||||
|       memcpy(eq->block + eq->block_ptr * 2, in, write_avail * 2 * sizeof(float)); | ||||
| 
 | ||||
|       in += write_avail * 2; | ||||
|       input_frames -= write_avail; | ||||
|       eq->block_ptr += write_avail; | ||||
| 
 | ||||
|       // Convolve a new block.
 | ||||
|       if (eq->block_ptr == eq->block_size) | ||||
|       { | ||||
|          unsigned i, c; | ||||
| 
 | ||||
|          for (c = 0; c < 2; c++) | ||||
|          { | ||||
|             fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2); | ||||
|             for (i = 0; i < 2 * eq->block_size; i++) | ||||
|                eq->fftblock[i] = fft_complex_mul(eq->fftblock[i], eq->filter[i]); | ||||
|             fft_process_inverse(eq->fft, out + c, eq->fftblock, 2); | ||||
|          } | ||||
| 
 | ||||
|          // Overlap add method, so add in saved block now.
 | ||||
|          for (i = 0; i < 2 * eq->block_size; i++) | ||||
|             out[i] += eq->save[i]; | ||||
| 
 | ||||
|          // Save block for later.
 | ||||
|          memcpy(eq->save, out + 2 * eq->block_size, 2 * eq->block_size * sizeof(float)); | ||||
| 
 | ||||
|          out += eq->block_size * 2; | ||||
|          output->frames += eq->block_size; | ||||
|          eq->block_ptr = 0; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static int gains_cmp(const void *a_, const void *b_) | ||||
| { | ||||
|    const struct eq_gain *a = (const struct eq_gain*)a_; | ||||
|    const struct eq_gain *b = (const struct eq_gain*)b_; | ||||
|    if (a->freq < b->freq) | ||||
|       return -1; | ||||
|    if (a->freq > b->freq) | ||||
|       return 1; | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| static void generate_response(fft_complex_t *response, | ||||
|       const struct eq_gain *gains, unsigned num_gains, unsigned samples) | ||||
| { | ||||
|    unsigned i; | ||||
| 
 | ||||
|    float start_freq = 0.0f; | ||||
|    float start_gain = 1.0f; | ||||
| 
 | ||||
|    float end_freq   = 1.0f; | ||||
|    float end_gain   = 1.0f; | ||||
| 
 | ||||
|    if (num_gains) | ||||
|    { | ||||
|       end_freq = gains->freq; | ||||
|       end_gain = gains->gain; | ||||
|       num_gains--; | ||||
|       gains++; | ||||
|    } | ||||
| 
 | ||||
|    /* Create a response by linear interpolation between
 | ||||
|     * known frequency sample points. */ | ||||
|    for (i = 0; i <= samples; i++) | ||||
|    { | ||||
|       float gain; | ||||
|       float lerp = 0.5f; | ||||
|       float freq = (float)i / samples; | ||||
| 
 | ||||
|       while (freq >= end_freq) | ||||
|       { | ||||
|          if (num_gains) | ||||
|          { | ||||
|             start_freq = end_freq; | ||||
|             start_gain = end_gain; | ||||
|             end_freq = gains->freq; | ||||
|             end_gain = gains->gain; | ||||
| 
 | ||||
|             gains++; | ||||
|             num_gains--; | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             start_freq = end_freq; | ||||
|             start_gain = end_gain; | ||||
|             end_freq = 1.0f; | ||||
|             end_gain = 1.0f; | ||||
|             break; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       /* Edge case where i == samples. */ | ||||
|       if (end_freq > start_freq) | ||||
|          lerp = (freq - start_freq) / (end_freq - start_freq); | ||||
|       gain = (1.0f - lerp) * start_gain + lerp * end_gain; | ||||
| 
 | ||||
|       response[i].real = gain; | ||||
|       response[i].imag = 0.0f; | ||||
|       response[2 * samples - i].real = gain; | ||||
|       response[2 * samples - i].imag = 0.0f; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void create_filter(struct eq_data *eq, unsigned size_log2, | ||||
|       struct eq_gain *gains, unsigned num_gains, double beta, const char *filter_path) | ||||
| { | ||||
|    int i; | ||||
|    int half_block_size = eq->block_size >> 1; | ||||
|    double window_mod = 1.0 / kaiser_window_function(0.0, beta); | ||||
| 
 | ||||
|    fft_t *fft = fft_new(size_log2); | ||||
|    float *time_filter = (float*)calloc(eq->block_size * 2 + 1, sizeof(*time_filter)); | ||||
|    if (!fft || !time_filter) | ||||
|       goto end; | ||||
| 
 | ||||
|    /* Make sure bands are in correct order. */ | ||||
|    qsort(gains, num_gains, sizeof(*gains), gains_cmp); | ||||
| 
 | ||||
|    /* Compute desired filter response. */ | ||||
|    generate_response(eq->filter, gains, num_gains, half_block_size); | ||||
| 
 | ||||
|    /* Get equivalent time-domain filter. */ | ||||
|    fft_process_inverse(fft, time_filter, eq->filter, 1); | ||||
| 
 | ||||
|    /* ifftshift() to create the correct linear phase filter.
 | ||||
|     * The filter response was designed with zero phase, which | ||||
|     * won't work unless we compensate | ||||
|     * for the repeating property of the FFT here | ||||
|     * by flipping left and right blocks. */ | ||||
|    for (i = 0; i < half_block_size; i++) | ||||
|    { | ||||
|       float tmp = time_filter[i + half_block_size]; | ||||
|       time_filter[i + half_block_size] = time_filter[i]; | ||||
|       time_filter[i] = tmp; | ||||
|    } | ||||
| 
 | ||||
|    /* Apply a window to smooth out the frequency repsonse. */ | ||||
|    for (i = 0; i < (int)eq->block_size; i++) | ||||
|    { | ||||
|       /* Kaiser window. */ | ||||
|       double phase = (double)i / eq->block_size; | ||||
|       phase = 2.0 * (phase - 0.5); | ||||
|       time_filter[i] *= window_mod * kaiser_window_function(phase, beta); | ||||
|    } | ||||
| 
 | ||||
|    /* Debugging. */ | ||||
|    if (filter_path) | ||||
|    { | ||||
|       FILE *file = fopen(filter_path, "w"); | ||||
|       if (file) | ||||
|       { | ||||
|          for (i = 0; i < (int)eq->block_size - 1; i++) | ||||
|             fprintf(file, "%.8f\n", time_filter[i + 1]); | ||||
|          fclose(file); | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    /* Padded FFT to create our FFT filter.
 | ||||
|     * Make our even-length filter odd by discarding the first coefficient. | ||||
|     * For some interesting reason, this allows us to design an odd-length linear phase filter. | ||||
|     */ | ||||
|    fft_process_forward(eq->fft, eq->filter, time_filter + 1, 1); | ||||
| 
 | ||||
| end: | ||||
|    fft_free(fft); | ||||
|    free(time_filter); | ||||
| } | ||||
| 
 | ||||
| static void *eq_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    float *frequencies, *gain; | ||||
|    unsigned num_freq, num_gain, i, size; | ||||
|    int size_log2; | ||||
|    float beta; | ||||
|    struct eq_gain *gains = NULL; | ||||
|    char *filter_path = NULL; | ||||
|    const float default_freq[] = { 0.0f, info->input_rate }; | ||||
|    const float default_gain[] = { 0.0f, 0.0f }; | ||||
|    struct eq_data *eq = (struct eq_data*)calloc(1, sizeof(*eq)); | ||||
|    if (!eq) | ||||
|       return NULL; | ||||
| 
 | ||||
| 
 | ||||
|    config->get_float(userdata, "window_beta", &beta, 4.0f); | ||||
| 
 | ||||
|    config->get_int(userdata, "block_size_log2", &size_log2, 8); | ||||
|    size = 1 << size_log2; | ||||
| 
 | ||||
|    config->get_float_array(userdata, "frequencies", &frequencies, &num_freq, default_freq, 2); | ||||
|    config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2); | ||||
| 
 | ||||
|    if (!config->get_string(userdata, "impulse_response_output", &filter_path, "")) | ||||
|    { | ||||
|       config->free(filter_path); | ||||
|       filter_path = NULL; | ||||
|    } | ||||
| 
 | ||||
|    num_gain = num_freq = MIN(num_gain, num_freq); | ||||
| 
 | ||||
|    gains = (struct eq_gain*)calloc(num_gain, sizeof(*gains)); | ||||
|    if (!gains) | ||||
|       goto error; | ||||
| 
 | ||||
|    for (i = 0; i < num_gain; i++) | ||||
|    { | ||||
|       gains[i].freq = frequencies[i] / (0.5f * info->input_rate); | ||||
|       gains[i].gain = pow(10.0, gain[i] / 20.0); | ||||
|    } | ||||
|    config->free(frequencies); | ||||
|    config->free(gain); | ||||
| 
 | ||||
|    eq->block_size = size; | ||||
| 
 | ||||
|    eq->save     = (float*)calloc(    size, 2 * sizeof(*eq->save)); | ||||
|    eq->block    = (float*)calloc(2 * size, 2 * sizeof(*eq->block)); | ||||
|    eq->fftblock = (fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock)); | ||||
|    eq->filter   = (fft_complex_t*)calloc(2 * size, sizeof(*eq->filter)); | ||||
| 
 | ||||
|    /* Use an FFT which is twice the block size with zero-padding
 | ||||
|     * to make circular convolution => proper convolution. | ||||
|     */ | ||||
|    eq->fft = fft_new(size_log2 + 1); | ||||
| 
 | ||||
|    if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter) | ||||
|       goto error; | ||||
| 
 | ||||
|    create_filter(eq, size_log2, gains, num_gain, beta, filter_path); | ||||
|    config->free(filter_path); | ||||
|    filter_path = NULL; | ||||
| 
 | ||||
|    free(gains); | ||||
|    return eq; | ||||
| 
 | ||||
| error: | ||||
|    free(gains); | ||||
|    eq_free(eq); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_implementation eq_plug = { | ||||
|    eq_init, | ||||
|    eq_process, | ||||
|    eq_free, | ||||
| 
 | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "Linear-Phase FFT Equalizer", | ||||
|    "eq", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation eq_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &eq_plug; | ||||
| } | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
|  | @ -0,0 +1,205 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (fft.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "fft.h" | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| 
 | ||||
| struct fft | ||||
| { | ||||
|    fft_complex_t *interleave_buffer; | ||||
|    fft_complex_t *phase_lut; | ||||
|    unsigned *bitinverse_buffer; | ||||
|    unsigned size; | ||||
| }; | ||||
| 
 | ||||
| static unsigned bitswap(unsigned x, unsigned size_log2) | ||||
| { | ||||
|    unsigned i; | ||||
|    unsigned ret = 0; | ||||
|    for (i = 0; i < size_log2; i++) | ||||
|       ret |= ((x >> i) & 1) << (size_log2 - i - 1); | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| static void build_bitinverse(unsigned *bitinverse, unsigned size_log2) | ||||
| { | ||||
|    unsigned i; | ||||
|    unsigned size = 1 << size_log2; | ||||
|    for (i = 0; i < size; i++) | ||||
|       bitinverse[i] = bitswap(i, size_log2); | ||||
| } | ||||
| 
 | ||||
| static fft_complex_t exp_imag(double phase) | ||||
| { | ||||
|    fft_complex_t out = { cos(phase), sin(phase) }; | ||||
|    return out; | ||||
| } | ||||
| 
 | ||||
| static void build_phase_lut(fft_complex_t *out, int size) | ||||
| { | ||||
|    int i; | ||||
|    out += size; | ||||
|    for (i = -size; i <= size; i++) | ||||
|       out[i] = exp_imag((M_PI * i) / size); | ||||
| } | ||||
| 
 | ||||
| static void interleave_complex(const unsigned *bitinverse, | ||||
|       fft_complex_t *out, const fft_complex_t *in, | ||||
|       unsigned samples, unsigned step) | ||||
| { | ||||
|    unsigned i; | ||||
|    for (i = 0; i < samples; i++, in += step) | ||||
|       out[bitinverse[i]] = *in; | ||||
| } | ||||
| 
 | ||||
| static void interleave_float(const unsigned *bitinverse, | ||||
|       fft_complex_t *out, const float *in, | ||||
|       unsigned samples, unsigned step) | ||||
| { | ||||
|    unsigned i; | ||||
|    for (i = 0; i < samples; i++, in += step) | ||||
|    { | ||||
|       unsigned inv_i = bitinverse[i]; | ||||
|       out[inv_i].real = *in; | ||||
|       out[inv_i].imag = 0.0f; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void resolve_float(float *out, const fft_complex_t *in, unsigned samples, | ||||
|       float gain, unsigned step) | ||||
| { | ||||
|    unsigned i; | ||||
|    for (i = 0; i < samples; i++, in++, out += step) | ||||
|       *out = gain * in->real; | ||||
| } | ||||
| 
 | ||||
| fft_t *fft_new(unsigned block_size_log2) | ||||
| { | ||||
|    unsigned size; | ||||
|    fft_t *fft = (fft_t*)calloc(1, sizeof(*fft)); | ||||
|    if (!fft) | ||||
|       return NULL; | ||||
| 
 | ||||
|    size                   = 1 << block_size_log2; | ||||
|    fft->interleave_buffer = (fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer)); | ||||
|    fft->bitinverse_buffer = (unsigned*)calloc(size, sizeof(*fft->bitinverse_buffer)); | ||||
|    fft->phase_lut         = (fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut)); | ||||
| 
 | ||||
|    if (!fft->interleave_buffer || !fft->bitinverse_buffer || !fft->phase_lut) | ||||
|       goto error; | ||||
| 
 | ||||
|    fft->size = size; | ||||
| 
 | ||||
|    build_bitinverse(fft->bitinverse_buffer, block_size_log2); | ||||
|    build_phase_lut(fft->phase_lut, size); | ||||
|    return fft; | ||||
| 
 | ||||
| error: | ||||
|    fft_free(fft); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| void fft_free(fft_t *fft) | ||||
| { | ||||
|    if (!fft) | ||||
|       return; | ||||
| 
 | ||||
|    free(fft->interleave_buffer); | ||||
|    free(fft->bitinverse_buffer); | ||||
|    free(fft->phase_lut); | ||||
|    free(fft); | ||||
| } | ||||
| 
 | ||||
| static void butterfly(fft_complex_t *a, fft_complex_t *b, fft_complex_t mod) | ||||
| { | ||||
|    mod = fft_complex_mul(mod, *b); | ||||
|    *b  = fft_complex_sub(*a, mod); | ||||
|    *a  = fft_complex_add(*a, mod); | ||||
| } | ||||
| 
 | ||||
| static void butterflies(fft_complex_t *butterfly_buf, | ||||
|       const fft_complex_t *phase_lut, | ||||
|       int phase_dir, unsigned step_size, unsigned samples) | ||||
| { | ||||
|    unsigned i, j; | ||||
|    for (i = 0; i < samples; i += step_size << 1) | ||||
|    { | ||||
|       int phase_step = (int)samples * phase_dir / (int)step_size; | ||||
|       for (j = i; j < i + step_size; j++) | ||||
|          butterfly(&butterfly_buf[j], &butterfly_buf[j + step_size], | ||||
|                phase_lut[phase_step * (int)(j - i)]); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void fft_process_forward_complex(fft_t *fft, | ||||
|       fft_complex_t *out, const fft_complex_t *in, unsigned step) | ||||
| { | ||||
|    unsigned step_size; | ||||
|    unsigned samples = fft->size; | ||||
|    interleave_complex(fft->bitinverse_buffer, out, in, samples, step); | ||||
| 
 | ||||
|    for (step_size = 1; step_size < samples; step_size <<= 1) | ||||
|    { | ||||
|       butterflies(out, | ||||
|             fft->phase_lut + samples, | ||||
|             -1, step_size, samples); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void fft_process_forward(fft_t *fft, | ||||
|       fft_complex_t *out, const float *in, unsigned step) | ||||
| { | ||||
|    unsigned step_size; | ||||
|    unsigned samples = fft->size; | ||||
|    interleave_float(fft->bitinverse_buffer, out, in, samples, step); | ||||
| 
 | ||||
|    for (step_size = 1; step_size < fft->size; step_size <<= 1) | ||||
|    { | ||||
|       butterflies(out, | ||||
|             fft->phase_lut + samples, | ||||
|             -1, step_size, samples); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void fft_process_inverse(fft_t *fft, | ||||
|       float *out, const fft_complex_t *in, unsigned step) | ||||
| { | ||||
|    unsigned step_size; | ||||
|    unsigned samples = fft->size; | ||||
| 
 | ||||
|    interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer, | ||||
|          in, samples, 1); | ||||
| 
 | ||||
|    for (step_size = 1; step_size < samples; step_size <<= 1) | ||||
|    { | ||||
|       butterflies(fft->interleave_buffer, | ||||
|             fft->phase_lut + samples, | ||||
|             1, step_size, samples); | ||||
|    } | ||||
| 
 | ||||
|    resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples, step); | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,46 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (fft.h). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef RARCH_FFT_H__ | ||||
| #define RARCH_FFT_H__ | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| #include <math/complex.h> | ||||
| 
 | ||||
| typedef struct fft fft_t; | ||||
| 
 | ||||
| fft_t *fft_new(unsigned block_size_log2); | ||||
| 
 | ||||
| void fft_free(fft_t *fft); | ||||
| 
 | ||||
| void fft_process_forward_complex(fft_t *fft, | ||||
|       fft_complex_t *out, const fft_complex_t *in, unsigned step); | ||||
| 
 | ||||
| void fft_process_forward(fft_t *fft, | ||||
|       fft_complex_t *out, const float *in, unsigned step); | ||||
| 
 | ||||
| void fft_process_inverse(fft_t *fft, | ||||
|       float *out, const fft_complex_t *in, unsigned step); | ||||
| 
 | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  | @ -0,0 +1,372 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (iir.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| #include <string/stdstring.h> | ||||
| 
 | ||||
| #define sqr(a) ((a) * (a)) | ||||
| 
 | ||||
| /* filter types */ | ||||
| enum IIRFilter | ||||
| { | ||||
|    LPF,        /* low pass filter */ | ||||
|    HPF,        /* High pass filter */ | ||||
|    BPCSGF,     /* band pass filter 1 */ | ||||
|    BPZPGF,     /* band pass filter 2 */ | ||||
|    APF,        /* Allpass filter*/ | ||||
|    NOTCH,      /* Notch Filter */ | ||||
|    RIAA_phono, /* RIAA record/tape deemphasis */ | ||||
|    PEQ,        /* Peaking band EQ filter */ | ||||
|    BBOOST,     /* Bassboost filter */ | ||||
|    LSH,        /* Low shelf filter */ | ||||
|    HSH,        /* High shelf filter */ | ||||
|    RIAA_CD     /* CD de-emphasis */ | ||||
| }; | ||||
| 
 | ||||
| struct iir_data | ||||
| { | ||||
|    float b0, b1, b2; | ||||
|    float a0, a1, a2; | ||||
| 
 | ||||
|    struct | ||||
|    { | ||||
|       float xn1, xn2; | ||||
|       float yn1, yn2; | ||||
|    } l, r; | ||||
| }; | ||||
| 
 | ||||
| static void iir_free(void *data) | ||||
| { | ||||
|    free(data); | ||||
| } | ||||
| 
 | ||||
| static void iir_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    unsigned i; | ||||
|    struct iir_data *iir = (struct iir_data*)data; | ||||
|    float *out           = output->samples; | ||||
| 
 | ||||
|    float b0             = iir->b0; | ||||
|    float b1             = iir->b1; | ||||
|    float b2             = iir->b2; | ||||
|    float a0             = iir->a0; | ||||
|    float a1             = iir->a1; | ||||
|    float a2             = iir->a2; | ||||
| 
 | ||||
|    float xn1_l          = iir->l.xn1; | ||||
|    float xn2_l          = iir->l.xn2; | ||||
|    float yn1_l          = iir->l.yn1; | ||||
|    float yn2_l          = iir->l.yn2; | ||||
| 
 | ||||
|    float xn1_r          = iir->r.xn1; | ||||
|    float xn2_r          = iir->r.xn2; | ||||
|    float yn1_r          = iir->r.yn1; | ||||
|    float yn2_r          = iir->r.yn2; | ||||
| 
 | ||||
|    output->samples      = input->samples; | ||||
|    output->frames       = input->frames; | ||||
| 
 | ||||
|    for (i = 0; i < input->frames; i++, out += 2) | ||||
|    { | ||||
|       float in_l = out[0]; | ||||
|       float in_r = out[1]; | ||||
| 
 | ||||
|       float l    = (b0 * in_l + b1 * xn1_l + b2 * xn2_l - a1 * yn1_l - a2 * yn2_l) / a0; | ||||
|       float r    = (b0 * in_r + b1 * xn1_r + b2 * xn2_r - a1 * yn1_r - a2 * yn2_r) / a0; | ||||
| 
 | ||||
|       xn2_l      = xn1_l; | ||||
|       xn1_l      = in_l; | ||||
|       yn2_l      = yn1_l; | ||||
|       yn1_l      = l; | ||||
| 
 | ||||
|       xn2_r      = xn1_r; | ||||
|       xn1_r      = in_r; | ||||
|       yn2_r      = yn1_r; | ||||
|       yn1_r      = r; | ||||
| 
 | ||||
|       out[0]     = l; | ||||
|       out[1]     = r; | ||||
|    } | ||||
| 
 | ||||
|    iir->l.xn1 = xn1_l; | ||||
|    iir->l.xn2 = xn2_l; | ||||
|    iir->l.yn1 = yn1_l; | ||||
|    iir->l.yn2 = yn2_l; | ||||
| 
 | ||||
|    iir->r.xn1 = xn1_r; | ||||
|    iir->r.xn2 = xn2_r; | ||||
|    iir->r.yn1 = yn1_r; | ||||
|    iir->r.yn2 = yn2_r; | ||||
| } | ||||
| 
 | ||||
| #define CHECK(x) if (string_is_equal(str, #x)) return x | ||||
| static enum IIRFilter str_to_type(const char *str) | ||||
| { | ||||
|    CHECK(LPF); | ||||
|    CHECK(HPF); | ||||
|    CHECK(BPCSGF); | ||||
|    CHECK(BPZPGF); | ||||
|    CHECK(APF); | ||||
|    CHECK(NOTCH); | ||||
|    CHECK(RIAA_phono); | ||||
|    CHECK(PEQ); | ||||
|    CHECK(BBOOST); | ||||
|    CHECK(LSH); | ||||
|    CHECK(HSH); | ||||
|    CHECK(RIAA_CD); | ||||
| 
 | ||||
|    return LPF; /* Fallback. */ | ||||
| } | ||||
| 
 | ||||
| static void make_poly_from_roots( | ||||
|       const double *roots, unsigned num_roots, float *poly) | ||||
| { | ||||
|    unsigned i, j; | ||||
| 
 | ||||
|    poly[0] = 1; | ||||
|    poly[1] = -roots[0]; | ||||
|    memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly)); | ||||
| 
 | ||||
|    for (i = 1; i < num_roots; i++) | ||||
|       for (j = num_roots; j > 0; j--) | ||||
|          poly[j] -= poly[j - 1] * roots[i]; | ||||
| } | ||||
| 
 | ||||
| static void iir_filter_init(struct iir_data *iir, | ||||
|       float sample_rate, float freq, float qual, float gain, enum IIRFilter filter_type) | ||||
| { | ||||
| 	double omega = 2.0 * M_PI * freq / sample_rate; | ||||
|    double cs    = cos(omega); | ||||
|    double sn    = sin(omega); | ||||
|    double a1pha = sn / (2.0 * qual); | ||||
|    double A     = exp(log(10.0) * gain / 40.0); | ||||
|    double beta  = sqrt(A + A); | ||||
| 
 | ||||
|    float b0     = 0.0, b1 = 0.0, b2 = 0.0, a0 = 0.0, a1 = 0.0, a2 = 0.0; | ||||
| 
 | ||||
|    /* Set up filter coefficients according to type */ | ||||
|    switch (filter_type) | ||||
|    { | ||||
|       case LPF: | ||||
|          b0 =  (1.0 - cs) / 2.0; | ||||
|          b1 =   1.0 - cs ; | ||||
|          b2 =  (1.0 - cs) / 2.0; | ||||
|          a0 =   1.0 + a1pha; | ||||
|          a1 =  -2.0 * cs; | ||||
|          a2 =   1.0 - a1pha; | ||||
|          break; | ||||
|       case HPF: | ||||
|          b0 =  (1.0 + cs) / 2.0; | ||||
|          b1 = -(1.0 + cs); | ||||
|          b2 =  (1.0 + cs) / 2.0; | ||||
|          a0 =   1.0 + a1pha; | ||||
|          a1 =  -2.0 * cs; | ||||
|          a2 =   1.0 - a1pha; | ||||
|          break; | ||||
|       case APF: | ||||
|          b0 =  1.0 - a1pha; | ||||
|          b1 = -2.0 * cs; | ||||
|          b2 =  1.0 + a1pha; | ||||
|          a0 =  1.0 + a1pha; | ||||
|          a1 = -2.0 * cs; | ||||
|          a2 =  1.0 - a1pha; | ||||
|          break; | ||||
|       case BPZPGF: | ||||
|          b0 =  a1pha; | ||||
|          b1 =  0.0; | ||||
|          b2 = -a1pha; | ||||
|          a0 =  1.0 + a1pha; | ||||
|          a1 = -2.0 * cs; | ||||
|          a2 =  1.0 - a1pha; | ||||
|          break; | ||||
|       case BPCSGF: | ||||
|          b0 =  sn / 2.0; | ||||
|          b1 =  0.0; | ||||
|          b2 = -sn / 2.0; | ||||
|          a0 =  1.0 + a1pha; | ||||
|          a1 = -2.0 * cs; | ||||
|          a2 =  1.0 - a1pha; | ||||
|          break; | ||||
|       case NOTCH: | ||||
|          b0 =  1.0; | ||||
|          b1 = -2.0 * cs; | ||||
|          b2 =  1.0; | ||||
|          a0 =  1.0 + a1pha; | ||||
|          a1 = -2.0 * cs; | ||||
|          a2 =  1.0 - a1pha; | ||||
|          break; | ||||
|       case RIAA_phono: /* http://www.dsprelated.com/showmessage/73300/3.php */ | ||||
|       { | ||||
|          double y, b_re, a_re, b_im, a_im, g; | ||||
|          float b[3], a[3]; | ||||
| 
 | ||||
|          if ((int)sample_rate == 44100) | ||||
|          { | ||||
|             static const double zeros[] = {-0.2014898, 0.9233820}; | ||||
|             static const double poles[] = {0.7083149, 0.9924091}; | ||||
|             make_poly_from_roots(zeros, 2, b); | ||||
|             make_poly_from_roots(poles, 2, a); | ||||
|          } | ||||
|          else if ((int)sample_rate == 48000) | ||||
|          { | ||||
|             static const double zeros[] = {-0.1766069, 0.9321590}; | ||||
|             static const double poles[] = {0.7396325, 0.9931330}; | ||||
|             make_poly_from_roots(zeros, 2, b); | ||||
|             make_poly_from_roots(poles, 2, a); | ||||
|          } | ||||
|          else if ((int)sample_rate == 88200) | ||||
|          { | ||||
|             static const double zeros[] = {-0.1168735, 0.9648312}; | ||||
|             static const double poles[] = {0.8590646, 0.9964002}; | ||||
|             make_poly_from_roots(zeros, 2, b); | ||||
|             make_poly_from_roots(poles, 2, a); | ||||
|          } | ||||
|          else if ((int)sample_rate == 96000) | ||||
|          { | ||||
|             static const double zeros[] = {-0.1141486, 0.9676817}; | ||||
|             static const double poles[] = {0.8699137, 0.9966946}; | ||||
|             make_poly_from_roots(zeros, 2, b); | ||||
|             make_poly_from_roots(poles, 2, a); | ||||
|          } | ||||
| 
 | ||||
|          b0    = b[0]; | ||||
|          b1    = b[1]; | ||||
|          b2    = b[2]; | ||||
|          a0    = a[0]; | ||||
|          a1    = a[1]; | ||||
|          a2    = a[2]; | ||||
| 
 | ||||
| 
 | ||||
|          /* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */ | ||||
|          y     = 2.0 * M_PI * 1000.0 / sample_rate; | ||||
|          b_re  = b0 + b1 * cos(-y) + b2 * cos(-2.0 * y); | ||||
|          a_re  = a0 + a1 * cos(-y) + a2 * cos(-2.0 * y); | ||||
|          b_im  = b1 * sin(-y) + b2 * sin(-2.0 * y); | ||||
|          a_im  = a1 * sin(-y) + a2 * sin(-2.0 * y); | ||||
|          g     = 1.0 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im))); | ||||
|          b0   *= g; b1 *= g; b2 *= g; | ||||
|          break; | ||||
|       } | ||||
|       case PEQ: | ||||
|          b0 =  1.0 + a1pha * A; | ||||
|          b1 = -2.0 * cs; | ||||
|          b2 =  1.0 - a1pha * A; | ||||
|          a0 =  1.0 + a1pha / A; | ||||
|          a1 = -2.0 * cs; | ||||
|          a2 =  1.0 - a1pha / A; | ||||
|          break; | ||||
|       case BBOOST: | ||||
|          beta = sqrt((A * A + 1) / 1.0 - (pow((A - 1), 2))); | ||||
|          b0 = A * ((A + 1) - (A - 1) * cs + beta * sn); | ||||
|          b1 = 2 * A * ((A - 1) - (A + 1) * cs); | ||||
|          b2 = A * ((A + 1) - (A - 1) * cs - beta * sn); | ||||
|          a0 = ((A + 1) + (A - 1) * cs + beta * sn); | ||||
|          a1 = -2 * ((A - 1) + (A + 1) * cs); | ||||
|          a2 = (A + 1) + (A - 1) * cs - beta * sn; | ||||
|          break; | ||||
|       case LSH: | ||||
|          b0 = A * ((A + 1) - (A - 1) * cs + beta * sn); | ||||
|          b1 = 2 * A * ((A - 1) - (A + 1) * cs); | ||||
|          b2 = A * ((A + 1) - (A - 1) * cs - beta * sn); | ||||
|          a0 = (A + 1) + (A - 1) * cs + beta * sn; | ||||
|          a1 = -2 * ((A - 1) + (A + 1) * cs); | ||||
|          a2 = (A + 1) + (A - 1) * cs - beta * sn; | ||||
|          break; | ||||
|       case RIAA_CD: | ||||
|          omega = 2.0 * M_PI * 5283.0 / sample_rate; | ||||
|          cs = cos(omega); | ||||
|          sn = sin(omega); | ||||
|          a1pha = sn / (2.0 * 0.4845); | ||||
|          A = exp(log(10.0) * -9.477 / 40.0); | ||||
|          beta = sqrt(A + A); | ||||
|          (void)a1pha; | ||||
|       case HSH: | ||||
|          b0 = A * ((A + 1.0) + (A - 1.0) * cs + beta * sn); | ||||
|          b1 = -2.0 * A * ((A - 1.0) + (A + 1.0) * cs); | ||||
|          b2 = A * ((A + 1.0) + (A - 1.0) * cs - beta * sn); | ||||
|          a0 = (A + 1.0) - (A - 1.0) * cs + beta * sn; | ||||
|          a1 = 2.0 * ((A - 1.0) - (A + 1.0) * cs); | ||||
|          a2 = (A + 1.0) - (A - 1.0) * cs - beta * sn; | ||||
|          break; | ||||
|       default: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    iir->b0 = b0; | ||||
|    iir->b1 = b1; | ||||
|    iir->b2 = b2; | ||||
|    iir->a0 = a0; | ||||
|    iir->a1 = a1; | ||||
|    iir->a2 = a2; | ||||
| } | ||||
| 
 | ||||
| static void *iir_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    float freq, qual, gain; | ||||
|    enum IIRFilter filter  = LPF; | ||||
|    char           *type   = NULL; | ||||
|    struct iir_data *iir   = (struct iir_data*)calloc(1, sizeof(*iir)); | ||||
|    if (!iir) | ||||
|       return NULL; | ||||
| 
 | ||||
|    config->get_float(userdata, "frequency", &freq, 1024.0f); | ||||
|    config->get_float(userdata, "quality", &qual, 0.707f); | ||||
|    config->get_float(userdata, "gain", &gain, 0.0f); | ||||
| 
 | ||||
|    config->get_string(userdata, "type", &type, "LPF"); | ||||
| 
 | ||||
|    filter = str_to_type(type); | ||||
|    config->free(type); | ||||
| 
 | ||||
|    iir_filter_init(iir, info->input_rate, freq, qual, gain, filter); | ||||
|    return iir; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_implementation iir_plug = { | ||||
|    iir_init, | ||||
|    iir_process, | ||||
|    iir_free, | ||||
| 
 | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "IIR", | ||||
|    "iir", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation iir_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &iir_plug; | ||||
| } | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
|  | @ -0,0 +1,4 @@ | |||
| { | ||||
|   global: dspfilter_get_implementation; | ||||
|   local: *; | ||||
| }; | ||||
|  | @ -0,0 +1,112 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (panning.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| struct panning_data | ||||
| { | ||||
|    float left[2]; | ||||
|    float right[2]; | ||||
| }; | ||||
| 
 | ||||
| static void panning_free(void *data) | ||||
| { | ||||
|    free(data); | ||||
| } | ||||
| 
 | ||||
| static void panning_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    unsigned i; | ||||
|    struct panning_data *pan = (struct panning_data*)data; | ||||
|    float *out               = output->samples; | ||||
| 
 | ||||
|    output->samples          = input->samples; | ||||
|    output->frames           = input->frames; | ||||
| 
 | ||||
|    for (i = 0; i < input->frames; i++, out += 2) | ||||
|    { | ||||
|       float left  = out[0]; | ||||
|       float right = out[1]; | ||||
|       out[0]      = left * pan->left[0]  + right * pan->left[1]; | ||||
|       out[1]      = left * pan->right[0] + right * pan->right[1]; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *panning_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    static const float default_left[]  = { 1.0f, 0.0f }; | ||||
|    static const float default_right[] = { 0.0f, 1.0f }; | ||||
|    float *left                        = NULL; | ||||
|    float *right                       = NULL; | ||||
|    unsigned num_left                  = 0; | ||||
|    unsigned num_right                 = 0; | ||||
|    struct panning_data *pan           = (struct panning_data*) | ||||
|       calloc(1, sizeof(*pan)); | ||||
| 
 | ||||
|    if (!pan) | ||||
|       return NULL; | ||||
| 
 | ||||
|    config->get_float_array(userdata, "left_mix", | ||||
|          &left, &num_left, default_left, 2); | ||||
|    config->get_float_array(userdata, "right_mix", | ||||
|          &right, &num_right, default_right, 2); | ||||
| 
 | ||||
|    memcpy(pan->left,  (num_left  == 2) ? | ||||
|          left :  default_left,  sizeof(pan->left)); | ||||
|    memcpy(pan->right, (num_right == 2) ? | ||||
|          right : default_right, sizeof(pan->right)); | ||||
| 
 | ||||
|    config->free(left); | ||||
|    config->free(right); | ||||
| 
 | ||||
|    return pan; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_implementation panning = { | ||||
|    panning_init, | ||||
|    panning_process, | ||||
|    panning_free, | ||||
| 
 | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "Panning", | ||||
|    "panning", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation panning_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation * | ||||
| dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &panning; | ||||
| } | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
|  | @ -0,0 +1,146 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (phaser.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| #define phaserlfoshape 4.0 | ||||
| #define phaserlfoskipsamples 20 | ||||
| 
 | ||||
| struct phaser_data | ||||
| { | ||||
|    float freq; | ||||
|    float startphase; | ||||
|    float fb; | ||||
|    float depth; | ||||
|    float drywet; | ||||
|    float old[2][24]; | ||||
|    float gain; | ||||
|    float fbout[2]; | ||||
|    float lfoskip; | ||||
|    float phase; | ||||
| 
 | ||||
|    int stages; | ||||
|    unsigned long skipcount; | ||||
| }; | ||||
| 
 | ||||
| static void phaser_free(void *data) | ||||
| { | ||||
|    free(data); | ||||
| } | ||||
| 
 | ||||
| static void phaser_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    unsigned i, c; | ||||
|    int s; | ||||
|    float m[2], tmp[2]; | ||||
|    struct phaser_data *ph = (struct phaser_data*)data; | ||||
|    float *out             = output->samples; | ||||
| 
 | ||||
|    output->samples        = input->samples; | ||||
|    output->frames         = input->frames; | ||||
| 
 | ||||
|    for (i = 0; i < input->frames; i++, out += 2) | ||||
|    { | ||||
|       float in[2] = { out[0], out[1] }; | ||||
| 
 | ||||
|       for (c = 0; c < 2; c++) | ||||
|          m[c] = in[c] + ph->fbout[c] * ph->fb * 0.01f; | ||||
| 
 | ||||
|       if ((ph->skipcount++ % phaserlfoskipsamples) == 0) | ||||
|       { | ||||
|          ph->gain = 0.5 * (1.0 + cos(ph->skipcount * ph->lfoskip + ph->phase)); | ||||
|          ph->gain = (exp(ph->gain * phaserlfoshape) - 1.0) / (exp(phaserlfoshape) - 1); | ||||
|          ph->gain = 1.0 - ph->gain * ph->depth; | ||||
|       } | ||||
| 
 | ||||
|       for (s = 0; s < ph->stages; s++) | ||||
|       { | ||||
|          for (c = 0; c < 2; c++) | ||||
|          { | ||||
|             tmp[c] = ph->old[c][s]; | ||||
|             ph->old[c][s] = ph->gain * tmp[c] + m[c]; | ||||
|             m[c] = tmp[c] - ph->gain * ph->old[c][s]; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       for (c = 0; c < 2; c++) | ||||
|       { | ||||
|          ph->fbout[c] = m[c]; | ||||
|          out[c] = m[c] * ph->drywet + in[c] * (1.0f - ph->drywet); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *phaser_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    float lfo_freq, lfo_start_phase; | ||||
|    struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph)); | ||||
|    if (!ph) | ||||
|       return NULL; | ||||
| 
 | ||||
|    config->get_float(userdata, "lfo_freq", &lfo_freq, 0.4f); | ||||
|    config->get_float(userdata, "lfo_start_phase", &lfo_start_phase, 0.0f); | ||||
|    config->get_float(userdata, "feedback", &ph->fb, 0.0f); | ||||
|    config->get_float(userdata, "depth", &ph->depth, 0.4f); | ||||
|    config->get_float(userdata, "dry_wet", &ph->drywet, 0.5f); | ||||
|    config->get_int(userdata, "stages", &ph->stages, 2); | ||||
| 
 | ||||
|    if (ph->stages < 1) | ||||
|       ph->stages = 1; | ||||
|    else if (ph->stages > 24) | ||||
|       ph->stages = 24; | ||||
| 
 | ||||
|    ph->lfoskip = lfo_freq * 2.0 * M_PI / info->input_rate; | ||||
|    ph->phase   = lfo_start_phase * M_PI / 180.0; | ||||
| 
 | ||||
|    return ph; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_implementation phaser_plug = { | ||||
|    phaser_init, | ||||
|    phaser_process, | ||||
|    phaser_free, | ||||
| 
 | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "Phaser", | ||||
|    "phaser", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation phaser_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &phaser_plug; | ||||
| } | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
|  | @ -0,0 +1,327 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (reverb.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| struct comb | ||||
| { | ||||
|    float *buffer; | ||||
|    unsigned bufsize; | ||||
|    unsigned bufidx; | ||||
| 
 | ||||
|    float feedback; | ||||
|    float filterstore; | ||||
|    float damp1, damp2; | ||||
| }; | ||||
| 
 | ||||
| struct allpass | ||||
| { | ||||
|    float *buffer; | ||||
|    float feedback; | ||||
|    unsigned bufsize; | ||||
|    unsigned bufidx; | ||||
| }; | ||||
| 
 | ||||
| static INLINE float comb_process(struct comb *c, float input) | ||||
| { | ||||
|    float output         = c->buffer[c->bufidx]; | ||||
|    c->filterstore       = (output * c->damp2) + (c->filterstore * c->damp1); | ||||
| 
 | ||||
|    c->buffer[c->bufidx] = input + (c->filterstore * c->feedback); | ||||
| 
 | ||||
|    c->bufidx++; | ||||
|    if (c->bufidx >= c->bufsize) | ||||
|       c->bufidx = 0; | ||||
| 
 | ||||
|    return output; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static INLINE float allpass_process(struct allpass *a, float input) | ||||
| { | ||||
|    float bufout         = a->buffer[a->bufidx]; | ||||
|    float output         = -input + bufout; | ||||
|    a->buffer[a->bufidx] = input + bufout * a->feedback; | ||||
| 
 | ||||
|    a->bufidx++; | ||||
|    if (a->bufidx >= a->bufsize) | ||||
|       a->bufidx = 0; | ||||
| 
 | ||||
|    return output; | ||||
| } | ||||
| 
 | ||||
| #define numcombs 8 | ||||
| #define numallpasses 4 | ||||
| static const float muted = 0; | ||||
| static const float fixedgain = 0.015f; | ||||
| static const float scalewet = 3; | ||||
| static const float scaledry = 2; | ||||
| static const float scaledamp = 0.4f; | ||||
| static const float scaleroom = 0.28f; | ||||
| static const float offsetroom = 0.7f; | ||||
| static const float initialroom = 0.5f; | ||||
| static const float initialdamp = 0.5f; | ||||
| static const float initialwet = 1.0f / 3.0f; | ||||
| static const float initialdry = 0; | ||||
| static const float initialwidth = 1; | ||||
| static const float initialmode = 0; | ||||
| static const float freezemode = 0.5f; | ||||
| 
 | ||||
| #define combtuningL1 1116 | ||||
| #define combtuningL2 1188 | ||||
| #define combtuningL3 1277 | ||||
| #define combtuningL4 1356 | ||||
| #define combtuningL5 1422 | ||||
| #define combtuningL6 1491 | ||||
| #define combtuningL7 1557 | ||||
| #define combtuningL8 1617 | ||||
| #define allpasstuningL1 556 | ||||
| #define allpasstuningL2 441 | ||||
| #define allpasstuningL3 341 | ||||
| #define allpasstuningL4 225 | ||||
| 
 | ||||
| struct revmodel | ||||
| { | ||||
|    struct comb combL[numcombs]; | ||||
|    struct allpass allpassL[numallpasses]; | ||||
| 
 | ||||
|    float bufcombL1[combtuningL1]; | ||||
|    float bufcombL2[combtuningL2]; | ||||
|    float bufcombL3[combtuningL3]; | ||||
|    float bufcombL4[combtuningL4]; | ||||
|    float bufcombL5[combtuningL5]; | ||||
|    float bufcombL6[combtuningL6]; | ||||
|    float bufcombL7[combtuningL7]; | ||||
|    float bufcombL8[combtuningL8]; | ||||
| 
 | ||||
|    float bufallpassL1[allpasstuningL1]; | ||||
|    float bufallpassL2[allpasstuningL2]; | ||||
|    float bufallpassL3[allpasstuningL3]; | ||||
|    float bufallpassL4[allpasstuningL4]; | ||||
| 
 | ||||
|    float gain; | ||||
|    float roomsize, roomsize1; | ||||
|    float damp, damp1; | ||||
|    float wet, wet1, wet2; | ||||
|    float dry; | ||||
|    float width; | ||||
|    float mode; | ||||
| }; | ||||
| 
 | ||||
| static float revmodel_process(struct revmodel *rev, float in) | ||||
| { | ||||
|    int i; | ||||
|    float mono_out = 0.0f; | ||||
|    float mono_in  = in; | ||||
|    float input    = mono_in * rev->gain; | ||||
| 
 | ||||
|    for (i = 0; i < numcombs; i++) | ||||
|       mono_out += comb_process(&rev->combL[i], input); | ||||
| 
 | ||||
|    for (i = 0; i < numallpasses; i++) | ||||
|       mono_out = allpass_process(&rev->allpassL[i], mono_out); | ||||
| 
 | ||||
|    return mono_in * rev->dry + mono_out * rev->wet1; | ||||
| } | ||||
| 
 | ||||
| static void revmodel_update(struct revmodel *rev) | ||||
| { | ||||
|    int i; | ||||
|    rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f); | ||||
| 
 | ||||
|    if (rev->mode >= freezemode) | ||||
|    { | ||||
|       rev->roomsize1 = 1.0f; | ||||
|       rev->damp1 = 0.0f; | ||||
|       rev->gain = muted; | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       rev->roomsize1 = rev->roomsize; | ||||
|       rev->damp1 = rev->damp; | ||||
|       rev->gain = fixedgain; | ||||
|    } | ||||
| 
 | ||||
|    for (i = 0; i < numcombs; i++) | ||||
|    { | ||||
|       rev->combL[i].feedback = rev->roomsize1; | ||||
|       rev->combL[i].damp1 = rev->damp1; | ||||
|       rev->combL[i].damp2 = 1.0f - rev->damp1; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void revmodel_setroomsize(struct revmodel *rev, float value) | ||||
| { | ||||
|    rev->roomsize = value * scaleroom + offsetroom; | ||||
|    revmodel_update(rev); | ||||
| } | ||||
| 
 | ||||
| static void revmodel_setdamp(struct revmodel *rev, float value) | ||||
| { | ||||
|    rev->damp = value * scaledamp; | ||||
|    revmodel_update(rev); | ||||
| } | ||||
| 
 | ||||
| static void revmodel_setwet(struct revmodel *rev, float value) | ||||
| { | ||||
|    rev->wet = value * scalewet; | ||||
|    revmodel_update(rev); | ||||
| } | ||||
| 
 | ||||
| static void revmodel_setdry(struct revmodel *rev, float value) | ||||
| { | ||||
|    rev->dry = value * scaledry; | ||||
|    revmodel_update(rev); | ||||
| } | ||||
| 
 | ||||
| static void revmodel_setwidth(struct revmodel *rev, float value) | ||||
| { | ||||
|    rev->width = value; | ||||
|    revmodel_update(rev); | ||||
| } | ||||
| 
 | ||||
| static void revmodel_setmode(struct revmodel *rev, float value) | ||||
| { | ||||
|    rev->mode = value; | ||||
|    revmodel_update(rev); | ||||
| } | ||||
| 
 | ||||
| static void revmodel_init(struct revmodel *rev) | ||||
| { | ||||
|    rev->combL[0].buffer      = rev->bufcombL1; rev->combL[0].bufsize = combtuningL1; | ||||
|    rev->combL[1].buffer      = rev->bufcombL2; rev->combL[1].bufsize = combtuningL2; | ||||
|    rev->combL[2].buffer      = rev->bufcombL3; rev->combL[2].bufsize = combtuningL3; | ||||
|    rev->combL[3].buffer      = rev->bufcombL4; rev->combL[3].bufsize = combtuningL4; | ||||
|    rev->combL[4].buffer      = rev->bufcombL5; rev->combL[4].bufsize = combtuningL5; | ||||
|    rev->combL[5].buffer      = rev->bufcombL6; rev->combL[5].bufsize = combtuningL6; | ||||
|    rev->combL[6].buffer      = rev->bufcombL7; rev->combL[6].bufsize = combtuningL7; | ||||
|    rev->combL[7].buffer      = rev->bufcombL8; rev->combL[7].bufsize = combtuningL8; | ||||
| 
 | ||||
|    rev->allpassL[0].buffer   = rev->bufallpassL1; rev->allpassL[0].bufsize = allpasstuningL1; | ||||
|    rev->allpassL[1].buffer   = rev->bufallpassL2; rev->allpassL[1].bufsize = allpasstuningL2; | ||||
|    rev->allpassL[2].buffer   = rev->bufallpassL3; rev->allpassL[2].bufsize = allpasstuningL3; | ||||
|    rev->allpassL[3].buffer   = rev->bufallpassL4; rev->allpassL[3].bufsize = allpasstuningL4; | ||||
| 
 | ||||
|    rev->allpassL[0].feedback = 0.5f; | ||||
|    rev->allpassL[1].feedback = 0.5f; | ||||
|    rev->allpassL[2].feedback = 0.5f; | ||||
|    rev->allpassL[3].feedback = 0.5f; | ||||
| 
 | ||||
|    revmodel_setwet(rev, initialwet); | ||||
|    revmodel_setroomsize(rev, initialroom); | ||||
|    revmodel_setdry(rev, initialdry); | ||||
|    revmodel_setdamp(rev, initialdamp); | ||||
|    revmodel_setwidth(rev, initialwidth); | ||||
|    revmodel_setmode(rev, initialmode); | ||||
| } | ||||
| 
 | ||||
| struct reverb_data | ||||
| { | ||||
|    struct revmodel left, right; | ||||
| }; | ||||
| 
 | ||||
| static void reverb_free(void *data) | ||||
| { | ||||
|    free(data); | ||||
| } | ||||
| 
 | ||||
| static void reverb_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    unsigned i; | ||||
|    float *out; | ||||
|    struct reverb_data *rev = (struct reverb_data*)data; | ||||
| 
 | ||||
|    output->samples         = input->samples; | ||||
|    output->frames          = input->frames; | ||||
|    out                     = output->samples; | ||||
| 
 | ||||
|    for (i = 0; i < input->frames; i++, out += 2) | ||||
|    { | ||||
|       float in[2] = { out[0], out[1] }; | ||||
| 
 | ||||
|       out[0] = revmodel_process(&rev->left, in[0]); | ||||
|       out[1] = revmodel_process(&rev->right, in[1]); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *reverb_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    float drytime, wettime, damping, roomwidth, roomsize; | ||||
|    struct reverb_data *rev = (struct reverb_data*) | ||||
|       calloc(1, sizeof(*rev)); | ||||
|    if (!rev) | ||||
|       return NULL; | ||||
| 
 | ||||
|    config->get_float(userdata, "drytime", &drytime, 0.43f); | ||||
|    config->get_float(userdata, "wettime", &wettime, 0.4f); | ||||
|    config->get_float(userdata, "damping", &damping, 0.8f); | ||||
|    config->get_float(userdata, "roomwidth", &roomwidth, 0.56f); | ||||
|    config->get_float(userdata, "roomsize", &roomsize, 0.56f); | ||||
| 
 | ||||
|    revmodel_init(&rev->left); | ||||
|    revmodel_init(&rev->right); | ||||
| 
 | ||||
|    revmodel_setdamp(&rev->left, damping); | ||||
|    revmodel_setdry(&rev->left, drytime); | ||||
|    revmodel_setwet(&rev->left, wettime); | ||||
|    revmodel_setwidth(&rev->left, roomwidth); | ||||
|    revmodel_setroomsize(&rev->left, roomsize); | ||||
| 
 | ||||
|    revmodel_setdamp(&rev->right, damping); | ||||
|    revmodel_setdry(&rev->right, drytime); | ||||
|    revmodel_setwet(&rev->right, wettime); | ||||
|    revmodel_setwidth(&rev->right, roomwidth); | ||||
|    revmodel_setroomsize(&rev->right, roomsize); | ||||
| 
 | ||||
|    return rev; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_implementation reverb_plug = { | ||||
|    reverb_init, | ||||
|    reverb_process, | ||||
|    reverb_free, | ||||
| 
 | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "Reverb", | ||||
|    "reverb", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation reverb_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &reverb_plug; | ||||
| } | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,148 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (wahwah.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <libretro_dspfilter.h> | ||||
| 
 | ||||
| #define WAHWAH_LFO_SKIP_SAMPLES 30 | ||||
| 
 | ||||
| struct wahwah_data | ||||
| { | ||||
|    float phase; | ||||
|    float lfoskip; | ||||
|    float b0, b1, b2, a0, a1, a2; | ||||
|    float freq, startphase; | ||||
|    float depth, freqofs, res; | ||||
|    unsigned long skipcount; | ||||
| 
 | ||||
|    struct | ||||
|    { | ||||
|       float xn1, xn2, yn1, yn2; | ||||
|    } l, r; | ||||
| }; | ||||
| 
 | ||||
| static void wahwah_free(void *data) | ||||
| { | ||||
|    if (data) | ||||
|       free(data); | ||||
| } | ||||
| 
 | ||||
| static void wahwah_process(void *data, struct dspfilter_output *output, | ||||
|       const struct dspfilter_input *input) | ||||
| { | ||||
|    unsigned i; | ||||
|    struct wahwah_data *wah = (struct wahwah_data*)data; | ||||
|    float *out              = output->samples; | ||||
| 
 | ||||
|    output->samples         = input->samples; | ||||
|    output->frames          = input->frames; | ||||
| 
 | ||||
|    for (i = 0; i < input->frames; i++, out += 2) | ||||
|    { | ||||
|       float out_l, out_r; | ||||
|       float in[2] = { out[0], out[1] }; | ||||
| 
 | ||||
|       if ((wah->skipcount++ % WAHWAH_LFO_SKIP_SAMPLES) == 0) | ||||
|       { | ||||
|          float omega, sn, cs, alpha; | ||||
|          float frequency = (1.0 + cos(wah->skipcount * wah->lfoskip + wah->phase)) / 2.0; | ||||
| 
 | ||||
|          frequency = frequency * wah->depth * (1.0 - wah->freqofs) + wah->freqofs; | ||||
|          frequency = exp((frequency - 1.0) * 6.0); | ||||
| 
 | ||||
|          omega     = M_PI * frequency; | ||||
|          sn        = sin(omega); | ||||
|          cs        = cos(omega); | ||||
|          alpha     = sn / (2.0 * wah->res); | ||||
| 
 | ||||
|          wah->b0   = (1.0 - cs) / 2.0; | ||||
|          wah->b1   = 1.0 - cs; | ||||
|          wah->b2   = (1.0 - cs) / 2.0; | ||||
|          wah->a0   = 1.0 + alpha; | ||||
|          wah->a1   = -2.0 * cs; | ||||
|          wah->a2   = 1.0 - alpha; | ||||
|       } | ||||
| 
 | ||||
|       out_l      = (wah->b0 * in[0] + wah->b1 * wah->l.xn1 + wah->b2 * wah->l.xn2 - wah->a1 * wah->l.yn1 - wah->a2 * wah->l.yn2) / wah->a0; | ||||
|       out_r      = (wah->b0 * in[1] + wah->b1 * wah->r.xn1 + wah->b2 * wah->r.xn2 - wah->a1 * wah->r.yn1 - wah->a2 * wah->r.yn2) / wah->a0; | ||||
| 
 | ||||
|       wah->l.xn2 = wah->l.xn1; | ||||
|       wah->l.xn1 = in[0]; | ||||
|       wah->l.yn2 = wah->l.yn1; | ||||
|       wah->l.yn1 = out_l; | ||||
| 
 | ||||
|       wah->r.xn2 = wah->r.xn1; | ||||
|       wah->r.xn1 = in[1]; | ||||
|       wah->r.yn2 = wah->r.yn1; | ||||
|       wah->r.yn1 = out_r; | ||||
| 
 | ||||
|       out[0]     = out_l; | ||||
|       out[1]     = out_r; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *wahwah_init(const struct dspfilter_info *info, | ||||
|       const struct dspfilter_config *config, void *userdata) | ||||
| { | ||||
|    struct wahwah_data *wah = (struct wahwah_data*)calloc(1, sizeof(*wah)); | ||||
|    if (!wah) | ||||
|       return NULL; | ||||
| 
 | ||||
|    config->get_float(userdata, "lfo_freq", &wah->freq, 1.5f); | ||||
|    config->get_float(userdata, "lfo_start_phase", &wah->startphase, 0.0f); | ||||
|    config->get_float(userdata, "freq_offset", &wah->freqofs, 0.3f); | ||||
|    config->get_float(userdata, "depth", &wah->depth, 0.7f); | ||||
|    config->get_float(userdata, "resonance", &wah->res, 2.5f); | ||||
| 
 | ||||
|    wah->lfoskip = wah->freq * 2.0 * M_PI / info->input_rate; | ||||
|    wah->phase   = wah->startphase * M_PI / 180.0; | ||||
| 
 | ||||
|    return wah; | ||||
| } | ||||
| 
 | ||||
| static const struct dspfilter_implementation wahwah_plug = { | ||||
|    wahwah_init, | ||||
|    wahwah_process, | ||||
|    wahwah_free, | ||||
| 
 | ||||
|    DSPFILTER_API_VERSION, | ||||
|    "Wah-Wah", | ||||
|    "wahwah", | ||||
| }; | ||||
| 
 | ||||
| #ifdef HAVE_FILTERS_BUILTIN | ||||
| #define dspfilter_get_implementation wahwah_dspfilter_get_implementation | ||||
| #endif | ||||
| 
 | ||||
| const struct dspfilter_implementation * | ||||
| dspfilter_get_implementation(dspfilter_simd_mask_t mask) | ||||
| { | ||||
|    (void)mask; | ||||
|    return &wahwah_plug; | ||||
| } | ||||
| 
 | ||||
| #undef dspfilter_get_implementation | ||||
| 
 | ||||
|  | @ -0,0 +1,172 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (audio_resampler.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <string/stdstring.h> | ||||
| #include <features/features_cpu.h> | ||||
| #include <file/config_file_userdata.h> | ||||
| 
 | ||||
| #include <audio/audio_resampler.h> | ||||
| 
 | ||||
| static const retro_resampler_t *resampler_drivers[] = { | ||||
|    &sinc_resampler, | ||||
| #ifdef HAVE_CC_RESAMPLER | ||||
|    &CC_resampler, | ||||
| #endif | ||||
|    &nearest_resampler, | ||||
|    &null_resampler, | ||||
|    NULL, | ||||
| }; | ||||
| 
 | ||||
| static const struct resampler_config resampler_config = { | ||||
|    config_userdata_get_float, | ||||
|    config_userdata_get_int, | ||||
|    config_userdata_get_float_array, | ||||
|    config_userdata_get_int_array, | ||||
|    config_userdata_get_string, | ||||
|    config_userdata_free, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * find_resampler_driver_index: | ||||
|  * @ident                      : Identifier of resampler driver to find. | ||||
|  * | ||||
|  * Finds resampler driver index by @ident name. | ||||
|  * | ||||
|  * Returns: resampler driver index if resampler driver was found, otherwise | ||||
|  * -1. | ||||
|  **/ | ||||
| static int find_resampler_driver_index(const char *ident) | ||||
| { | ||||
|    unsigned i; | ||||
| 
 | ||||
|    for (i = 0; resampler_drivers[i]; i++) | ||||
|       if (string_is_equal_noncase(ident, resampler_drivers[i]->ident)) | ||||
|          return i; | ||||
|    return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * audio_resampler_driver_find_handle: | ||||
|  * @idx                : index of driver to get handle to. | ||||
|  * | ||||
|  * Returns: handle to audio resampler driver at index. Can be NULL | ||||
|  * if nothing found. | ||||
|  **/ | ||||
| const void *audio_resampler_driver_find_handle(int idx) | ||||
| { | ||||
|    const void *drv = resampler_drivers[idx]; | ||||
|    if (!drv) | ||||
|       return NULL; | ||||
|    return drv; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * audio_resampler_driver_find_ident: | ||||
|  * @idx                : index of driver to get handle to. | ||||
|  * | ||||
|  * Returns: Human-readable identifier of audio resampler driver at index. | ||||
|  * Can be NULL if nothing found. | ||||
|  **/ | ||||
| const char *audio_resampler_driver_find_ident(int idx) | ||||
| { | ||||
|    const retro_resampler_t *drv = resampler_drivers[idx]; | ||||
|    if (!drv) | ||||
|       return NULL; | ||||
|    return drv->ident; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * find_resampler_driver: | ||||
|  * @ident                      : Identifier of resampler driver to find. | ||||
|  * | ||||
|  * Finds resampler by @ident name. | ||||
|  * | ||||
|  * Returns: resampler driver if resampler driver was found, otherwise | ||||
|  * NULL. | ||||
|  **/ | ||||
| static const retro_resampler_t *find_resampler_driver(const char *ident) | ||||
| { | ||||
|    int i = find_resampler_driver_index(ident); | ||||
| 
 | ||||
|    if (i >= 0) | ||||
|       return resampler_drivers[i]; | ||||
| 
 | ||||
|    return resampler_drivers[0]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * resampler_append_plugs: | ||||
|  * @re                         : Resampler handle | ||||
|  * @backend                    : Resampler backend that is about to be set. | ||||
|  * @bw_ratio                   : Bandwidth ratio. | ||||
|  * | ||||
|  * Initializes resampler driver based on queried CPU features. | ||||
|  * | ||||
|  * Returns: true (1) if successfully initialized, otherwise false (0). | ||||
|  **/ | ||||
| static bool resampler_append_plugs(void **re, | ||||
|       const retro_resampler_t **backend, | ||||
|       enum resampler_quality quality, | ||||
|       double bw_ratio) | ||||
| { | ||||
|    resampler_simd_mask_t mask = (resampler_simd_mask_t)cpu_features_get(); | ||||
| 
 | ||||
|    if (*backend) | ||||
|       *re = (*backend)->init(&resampler_config, bw_ratio, quality, mask); | ||||
| 
 | ||||
|    if (!*re) | ||||
|       return false; | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * retro_resampler_realloc: | ||||
|  * @re                         : Resampler handle | ||||
|  * @backend                    : Resampler backend that is about to be set. | ||||
|  * @ident                      : Identifier name for resampler we want. | ||||
|  * @bw_ratio                   : Bandwidth ratio. | ||||
|  * | ||||
|  * Reallocates resampler. Will free previous handle before | ||||
|  * allocating a new one. If ident is NULL, first resampler will be used. | ||||
|  * | ||||
|  * Returns: true (1) if successful, otherwise false (0). | ||||
|  **/ | ||||
| bool retro_resampler_realloc(void **re, const retro_resampler_t **backend, | ||||
|       const char *ident, enum resampler_quality quality, double bw_ratio) | ||||
| { | ||||
|    if (*re && *backend) | ||||
|       (*backend)->free(*re); | ||||
| 
 | ||||
|    *re      = NULL; | ||||
|    *backend = find_resampler_driver(ident); | ||||
| 
 | ||||
|    if (!resampler_append_plugs(re, backend, quality, bw_ratio)) | ||||
|    { | ||||
|       if (!*re) | ||||
|          *backend = NULL; | ||||
|       return false; | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
|  | @ -0,0 +1,90 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (nearest_resampler.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <math.h> | ||||
| 
 | ||||
| #include <audio/audio_resampler.h> | ||||
| 
 | ||||
| typedef struct rarch_nearest_resampler | ||||
| { | ||||
|    float fraction; | ||||
| } rarch_nearest_resampler_t; | ||||
| 
 | ||||
| static void resampler_nearest_process( | ||||
|       void *re_, struct resampler_data *data) | ||||
| { | ||||
|    rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_; | ||||
|    audio_frame_float_t  *inp     = (audio_frame_float_t*)data->data_in; | ||||
|    audio_frame_float_t  *inp_max = (audio_frame_float_t*)inp + data->input_frames; | ||||
|    audio_frame_float_t  *outp    = (audio_frame_float_t*)data->data_out; | ||||
|    float                   ratio = 1.0 / data->ratio; | ||||
| 
 | ||||
|    while(inp != inp_max) | ||||
|    { | ||||
|       while(re->fraction > 1) | ||||
|       { | ||||
|          *outp++ = *inp; | ||||
|          re->fraction -= ratio; | ||||
|       } | ||||
|       re->fraction++; | ||||
|       inp++; | ||||
|    } | ||||
| 
 | ||||
|    data->output_frames = (outp - (audio_frame_float_t*)data->data_out); | ||||
| } | ||||
| 
 | ||||
| static void resampler_nearest_free(void *re_) | ||||
| { | ||||
|    rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_; | ||||
|    if (re) | ||||
|       free(re); | ||||
| } | ||||
| 
 | ||||
| static void *resampler_nearest_init(const struct resampler_config *config, | ||||
|       double bandwidth_mod,  | ||||
|       enum resampler_quality quality, | ||||
|       resampler_simd_mask_t mask) | ||||
| { | ||||
|    rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*) | ||||
|       calloc(1, sizeof(rarch_nearest_resampler_t)); | ||||
| 
 | ||||
|    (void)config; | ||||
|    (void)mask; | ||||
| 
 | ||||
|    if (!re) | ||||
|       return NULL; | ||||
| 
 | ||||
|    re->fraction = 0; | ||||
| 
 | ||||
|    return re; | ||||
| } | ||||
| 
 | ||||
| retro_resampler_t nearest_resampler = { | ||||
|    resampler_nearest_init, | ||||
|    resampler_nearest_process, | ||||
|    resampler_nearest_free, | ||||
|    RESAMPLER_API_VERSION, | ||||
|    "nearest", | ||||
|    "nearest" | ||||
| }; | ||||
|  | @ -0,0 +1,58 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (null_resampler.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <math.h> | ||||
| 
 | ||||
| #include <audio/audio_resampler.h> | ||||
| 
 | ||||
| typedef struct rarch_null_resampler | ||||
| { | ||||
|    void *empty; | ||||
| } rarch_null_resampler_t; | ||||
| 
 | ||||
| static void resampler_null_process( | ||||
|       void *re_, struct resampler_data *data) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void resampler_null_free(void *re_) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void *resampler_null_init(const struct resampler_config *config, | ||||
|       double bandwidth_mod,  | ||||
|       enum resampler_quality quality, | ||||
|       resampler_simd_mask_t mask) | ||||
| { | ||||
|    return (void*)0; | ||||
| } | ||||
| 
 | ||||
| retro_resampler_t null_resampler = { | ||||
|    resampler_null_init, | ||||
|    resampler_null_process, | ||||
|    resampler_null_free, | ||||
|    RESAMPLER_API_VERSION, | ||||
|    "null", | ||||
|    "null" | ||||
| }; | ||||
|  | @ -0,0 +1,720 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (sinc_resampler.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| /* Bog-standard windowed SINC implementation. */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <math.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| #include <filters.h> | ||||
| #include <memalign.h> | ||||
| 
 | ||||
| #include <audio/audio_resampler.h> | ||||
| #include <filters.h> | ||||
| 
 | ||||
| #ifdef __SSE__ | ||||
| #include <xmmintrin.h> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__AVX__) | ||||
| #include <immintrin.h> | ||||
| #endif | ||||
| 
 | ||||
| /* Rough SNR values for upsampling:
 | ||||
|  * LOWEST: 40 dB | ||||
|  * LOWER: 55 dB | ||||
|  * NORMAL: 70 dB | ||||
|  * HIGHER: 110 dB | ||||
|  * HIGHEST: 140 dB | ||||
|  */ | ||||
| 
 | ||||
| /* TODO, make all this more configurable. */ | ||||
| 
 | ||||
| enum sinc_window | ||||
| { | ||||
|    SINC_WINDOW_NONE   = 0, | ||||
|    SINC_WINDOW_KAISER, | ||||
|    SINC_WINDOW_LANCZOS | ||||
| }; | ||||
| 
 | ||||
| /* For the little amount of taps we're using,
 | ||||
|  * SSE1 is faster than AVX for some reason. | ||||
|  * AVX code is kept here though as by increasing number | ||||
|  * of sinc taps, the AVX code is clearly faster than SSE1. | ||||
|  */ | ||||
| 
 | ||||
| typedef struct rarch_sinc_resampler | ||||
| { | ||||
|    unsigned enable_avx; | ||||
|    unsigned phase_bits; | ||||
|    unsigned subphase_bits; | ||||
|    unsigned subphase_mask; | ||||
|    unsigned taps; | ||||
|    unsigned ptr; | ||||
|    uint32_t time; | ||||
|    float subphase_mod; | ||||
|    float kaiser_beta; | ||||
|    enum sinc_window window_type; | ||||
| 
 | ||||
|    /* A buffer for phase_table, buffer_l and buffer_r
 | ||||
|     * are created in a single calloc(). | ||||
|     * Ensure that we get as good cache locality as we can hope for. */ | ||||
|    float *main_buffer; | ||||
|    float *phase_table; | ||||
|    float *buffer_l; | ||||
|    float *buffer_r; | ||||
| } rarch_sinc_resampler_t; | ||||
| 
 | ||||
| #if defined(__ARM_NEON__) | ||||
| #if TARGET_OS_IPHONE | ||||
| #else | ||||
| #define WANT_NEON | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| #ifdef WANT_NEON | ||||
| /* Assumes that taps >= 8, and that taps is a multiple of 8. */ | ||||
| void process_sinc_neon_asm(float *out, const float *left, | ||||
|       const float *right, const float *coeff, unsigned taps); | ||||
| 
 | ||||
| static void resampler_sinc_process_neon(void *re_, struct resampler_data *data) | ||||
| { | ||||
|    rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; | ||||
|    unsigned phases                = 1 << (resamp->phase_bits + resamp->subphase_bits); | ||||
| 
 | ||||
|    uint32_t ratio                 = phases / data->ratio; | ||||
|    const float *input             = data->data_in; | ||||
|    float *output                  = data->data_out; | ||||
|    size_t frames                  = data->input_frames; | ||||
|    size_t out_frames              = 0; | ||||
| 
 | ||||
|    while (frames) | ||||
|    { | ||||
|       while (frames && resamp->time >= phases) | ||||
|       { | ||||
|          /* Push in reverse to make filter more obvious. */ | ||||
|          if (!resamp->ptr) | ||||
|             resamp->ptr = resamp->taps; | ||||
|          resamp->ptr--; | ||||
| 
 | ||||
|          resamp->buffer_l[resamp->ptr + resamp->taps] = | ||||
|          resamp->buffer_l[resamp->ptr]                = *input++; | ||||
| 
 | ||||
|          resamp->buffer_r[resamp->ptr + resamp->taps] = | ||||
|          resamp->buffer_r[resamp->ptr]                = *input++; | ||||
| 
 | ||||
|          resamp->time                                -= phases; | ||||
|          frames--; | ||||
|       } | ||||
| 
 | ||||
|       while (resamp->time < phases) | ||||
|       { | ||||
|          const float *buffer_l    = resamp->buffer_l + resamp->ptr; | ||||
|          const float *buffer_r    = resamp->buffer_r + resamp->ptr; | ||||
|          unsigned taps            = resamp->taps; | ||||
|          unsigned phase           = resamp->time >> resamp->subphase_bits; | ||||
|          const float *phase_table = resamp->phase_table + phase * taps; | ||||
| 
 | ||||
|          process_sinc_neon_asm(output, buffer_l, buffer_r, phase_table, taps); | ||||
| 
 | ||||
|          output += 2; | ||||
|          out_frames++; | ||||
|          resamp->time += ratio; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    data->output_frames = out_frames; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__AVX__) | ||||
| static void resampler_sinc_process_avx(void *re_, struct resampler_data *data) | ||||
| { | ||||
|    rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; | ||||
|    unsigned phases                = 1 << (resamp->phase_bits + resamp->subphase_bits); | ||||
| 
 | ||||
|    uint32_t ratio                 = phases / data->ratio; | ||||
|    const float *input             = data->data_in; | ||||
|    float *output                  = data->data_out; | ||||
|    size_t frames                  = data->input_frames; | ||||
|    size_t out_frames              = 0; | ||||
| 
 | ||||
|    while (frames) | ||||
|    { | ||||
|       while (frames && resamp->time >= phases) | ||||
|       { | ||||
|          /* Push in reverse to make filter more obvious. */ | ||||
|          if (!resamp->ptr) | ||||
|             resamp->ptr = resamp->taps; | ||||
|          resamp->ptr--; | ||||
| 
 | ||||
|          resamp->buffer_l[resamp->ptr + resamp->taps] = | ||||
|          resamp->buffer_l[resamp->ptr]                = *input++; | ||||
| 
 | ||||
|          resamp->buffer_r[resamp->ptr + resamp->taps] = | ||||
|          resamp->buffer_r[resamp->ptr]                = *input++; | ||||
| 
 | ||||
|          resamp->time                                -= phases; | ||||
|          frames--; | ||||
|       } | ||||
| 
 | ||||
|       while (resamp->time < phases) | ||||
|       { | ||||
|          unsigned i; | ||||
|          __m256 delta, sum_l, sum_r; | ||||
|          float *delta_table       = NULL; | ||||
|          float *phase_table       = NULL; | ||||
|          const float *buffer_l    = resamp->buffer_l + resamp->ptr; | ||||
|          const float *buffer_r    = resamp->buffer_r + resamp->ptr; | ||||
|          unsigned taps            = resamp->taps; | ||||
|          unsigned phase           = resamp->time >> resamp->subphase_bits; | ||||
| 
 | ||||
|          phase_table              = resamp->phase_table + phase * taps; | ||||
| 
 | ||||
|          if (resamp->window_type == SINC_WINDOW_KAISER) | ||||
|          { | ||||
|             phase_table              = resamp->phase_table + phase * taps * 2; | ||||
|             delta_table              = phase_table + taps; | ||||
|             delta                    = _mm256_set1_ps((float) | ||||
|                   (resamp->time & resamp->subphase_mask) * resamp->subphase_mod); | ||||
|          } | ||||
| 
 | ||||
|          sum_l                    = _mm256_setzero_ps(); | ||||
|          sum_r                    = _mm256_setzero_ps(); | ||||
| 
 | ||||
|          for (i = 0; i < taps; i += 8) | ||||
|          { | ||||
|             __m256 sinc; | ||||
|             __m256 buf_l  = _mm256_loadu_ps(buffer_l + i); | ||||
|             __m256 buf_r  = _mm256_loadu_ps(buffer_r + i); | ||||
| 
 | ||||
|             if (resamp->window_type == SINC_WINDOW_KAISER) | ||||
|             { | ||||
|                __m256 deltas = _mm256_load_ps(delta_table + i); | ||||
|                sinc          = _mm256_add_ps(_mm256_load_ps((const float*)phase_table + i), | ||||
|                      _mm256_mul_ps(deltas, delta)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                sinc          = _mm256_load_ps((const float*)phase_table + i); | ||||
|             } | ||||
| 
 | ||||
|             sum_l         = _mm256_add_ps(sum_l, _mm256_mul_ps(buf_l, sinc)); | ||||
|             sum_r         = _mm256_add_ps(sum_r, _mm256_mul_ps(buf_r, sinc)); | ||||
|          } | ||||
| 
 | ||||
|          /* hadd on AVX is weird, and acts on low-lanes
 | ||||
|           * and high-lanes separately. */ | ||||
|          __m256 res_l = _mm256_hadd_ps(sum_l, sum_l); | ||||
|          __m256 res_r = _mm256_hadd_ps(sum_r, sum_r); | ||||
|          res_l        = _mm256_hadd_ps(res_l, res_l); | ||||
|          res_r        = _mm256_hadd_ps(res_r, res_r); | ||||
|          res_l        = _mm256_add_ps(_mm256_permute2f128_ps(res_l, res_l, 1), res_l); | ||||
|          res_r        = _mm256_add_ps(_mm256_permute2f128_ps(res_r, res_r, 1), res_r); | ||||
| 
 | ||||
|          /* This is optimized to mov %xmmN, [mem].
 | ||||
|           * There doesn't seem to be any _mm256_store_ss intrinsic. */ | ||||
|          _mm_store_ss(output + 0, _mm256_extractf128_ps(res_l, 0)); | ||||
|          _mm_store_ss(output + 1, _mm256_extractf128_ps(res_r, 0)); | ||||
| 
 | ||||
|          output += 2; | ||||
|          out_frames++; | ||||
|          resamp->time += ratio; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    data->output_frames = out_frames; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__SSE__) | ||||
| static void resampler_sinc_process_sse(void *re_, struct resampler_data *data) | ||||
| { | ||||
|    rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; | ||||
|    unsigned phases                = 1 << (resamp->phase_bits + resamp->subphase_bits); | ||||
| 
 | ||||
|    uint32_t ratio                 = phases / data->ratio; | ||||
|    const float *input             = data->data_in; | ||||
|    float *output                  = data->data_out; | ||||
|    size_t frames                  = data->input_frames; | ||||
|    size_t out_frames              = 0; | ||||
| 
 | ||||
|    while (frames) | ||||
|    { | ||||
|       while (frames && resamp->time >= phases) | ||||
|       { | ||||
|          /* Push in reverse to make filter more obvious. */ | ||||
|          if (!resamp->ptr) | ||||
|             resamp->ptr = resamp->taps; | ||||
|          resamp->ptr--; | ||||
| 
 | ||||
|          resamp->buffer_l[resamp->ptr + resamp->taps] = | ||||
|          resamp->buffer_l[resamp->ptr]                = *input++; | ||||
| 
 | ||||
|          resamp->buffer_r[resamp->ptr + resamp->taps] = | ||||
|          resamp->buffer_r[resamp->ptr]                = *input++; | ||||
| 
 | ||||
|          resamp->time                                -= phases; | ||||
|          frames--; | ||||
|       } | ||||
| 
 | ||||
|       while (resamp->time < phases) | ||||
|       { | ||||
|          unsigned i; | ||||
|          __m128 sum, sum_l, sum_r, delta; | ||||
|          float *phase_table       = NULL; | ||||
|          float *delta_table       = NULL; | ||||
|          const float *buffer_l    = resamp->buffer_l + resamp->ptr; | ||||
|          const float *buffer_r    = resamp->buffer_r + resamp->ptr; | ||||
|          unsigned taps            = resamp->taps; | ||||
|          unsigned phase           = resamp->time >> resamp->subphase_bits; | ||||
| 
 | ||||
|          if (resamp->window_type == SINC_WINDOW_KAISER) | ||||
|          { | ||||
|             phase_table              = resamp->phase_table + phase * taps * 2; | ||||
|             delta_table              = phase_table + taps; | ||||
|             delta                    = _mm_set1_ps((float) | ||||
|                   (resamp->time & resamp->subphase_mask) * resamp->subphase_mod); | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             phase_table              = resamp->phase_table + phase * taps; | ||||
|          } | ||||
| 
 | ||||
|          sum_l                    = _mm_setzero_ps(); | ||||
|          sum_r                    = _mm_setzero_ps(); | ||||
| 
 | ||||
|          for (i = 0; i < taps; i += 4) | ||||
|          { | ||||
|             __m128 deltas, _sinc; | ||||
|             __m128 buf_l = _mm_loadu_ps(buffer_l + i); | ||||
|             __m128 buf_r = _mm_loadu_ps(buffer_r + i); | ||||
| 
 | ||||
|             if (resamp->window_type == SINC_WINDOW_KAISER) | ||||
|             { | ||||
|                deltas = _mm_load_ps(delta_table + i); | ||||
|                _sinc  = _mm_add_ps(_mm_load_ps((const float*)phase_table + i), | ||||
|                      _mm_mul_ps(deltas, delta)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                _sinc  = _mm_load_ps((const float*)phase_table + i); | ||||
|             } | ||||
|             sum_l        = _mm_add_ps(sum_l, _mm_mul_ps(buf_l, _sinc)); | ||||
|             sum_r        = _mm_add_ps(sum_r, _mm_mul_ps(buf_r, _sinc)); | ||||
|          } | ||||
| 
 | ||||
|          /* Them annoying shuffles.
 | ||||
|           * sum_l = { l3, l2, l1, l0 } | ||||
|           * sum_r = { r3, r2, r1, r0 } | ||||
|           */ | ||||
| 
 | ||||
|          sum = _mm_add_ps(_mm_shuffle_ps(sum_l, sum_r, | ||||
|                   _MM_SHUFFLE(1, 0, 1, 0)), | ||||
|                _mm_shuffle_ps(sum_l, sum_r, _MM_SHUFFLE(3, 2, 3, 2))); | ||||
| 
 | ||||
|          /* sum   = { r1, r0, l1, l0 } + { r3, r2, l3, l2 }
 | ||||
|           * sum   = { R1, R0, L1, L0 } | ||||
|           */ | ||||
| 
 | ||||
|          sum = _mm_add_ps(_mm_shuffle_ps(sum, sum, _MM_SHUFFLE(3, 3, 1, 1)), sum); | ||||
| 
 | ||||
|          /* sum   = {R1, R1, L1, L1 } + { R1, R0, L1, L0 }
 | ||||
|           * sum   = { X,  R,  X,  L } | ||||
|           */ | ||||
| 
 | ||||
|          /* Store L */ | ||||
|          _mm_store_ss(output + 0, sum); | ||||
| 
 | ||||
|          /* movehl { X, R, X, L } == { X, R, X, R } */ | ||||
|          _mm_store_ss(output + 1, _mm_movehl_ps(sum, sum)); | ||||
| 
 | ||||
|          output += 2; | ||||
|          out_frames++; | ||||
|          resamp->time += ratio; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    data->output_frames = out_frames; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static void resampler_sinc_process_c(void *re_, struct resampler_data *data) | ||||
| { | ||||
|    rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; | ||||
|    unsigned phases                = 1 << (resamp->phase_bits + resamp->subphase_bits); | ||||
| 
 | ||||
|    uint32_t ratio                 = phases / data->ratio; | ||||
|    const float *input             = data->data_in; | ||||
|    float *output                  = data->data_out; | ||||
|    size_t frames                  = data->input_frames; | ||||
|    size_t out_frames              = 0; | ||||
| 
 | ||||
|    while (frames) | ||||
|    { | ||||
|       while (frames && resamp->time >= phases) | ||||
|       { | ||||
|          /* Push in reverse to make filter more obvious. */ | ||||
|          if (!resamp->ptr) | ||||
|             resamp->ptr = resamp->taps; | ||||
|          resamp->ptr--; | ||||
| 
 | ||||
|          resamp->buffer_l[resamp->ptr + resamp->taps]    = | ||||
|             resamp->buffer_l[resamp->ptr]                = *input++; | ||||
| 
 | ||||
|          resamp->buffer_r[resamp->ptr + resamp->taps]    = | ||||
|             resamp->buffer_r[resamp->ptr]                = *input++; | ||||
| 
 | ||||
|          resamp->time                                   -= phases; | ||||
|          frames--; | ||||
|       } | ||||
| 
 | ||||
|       while (resamp->time < phases) | ||||
|       { | ||||
|          unsigned i; | ||||
|          float delta              = 0.0f; | ||||
|          float sum_l              = 0.0f; | ||||
|          float sum_r              = 0.0f; | ||||
|          float *phase_table       = NULL; | ||||
|          float *delta_table       = NULL; | ||||
|          const float *buffer_l    = resamp->buffer_l + resamp->ptr; | ||||
|          const float *buffer_r    = resamp->buffer_r + resamp->ptr; | ||||
|          unsigned taps            = resamp->taps; | ||||
|          unsigned phase           = resamp->time >> resamp->subphase_bits; | ||||
| 
 | ||||
|          if (resamp->window_type == SINC_WINDOW_KAISER) | ||||
|          { | ||||
|             phase_table           = resamp->phase_table + phase * taps * 2; | ||||
|             delta_table           = phase_table + taps; | ||||
|             delta                 = (float) | ||||
|                (resamp->time & resamp->subphase_mask) * resamp->subphase_mod; | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             phase_table           = resamp->phase_table + phase * taps; | ||||
|          } | ||||
| 
 | ||||
|          for (i = 0; i < taps; i++) | ||||
|          { | ||||
|             float sinc_val        = phase_table[i]; | ||||
| 
 | ||||
|             if (resamp->window_type == SINC_WINDOW_KAISER) | ||||
|                sinc_val           = sinc_val + delta_table[i] * delta; | ||||
| 
 | ||||
|             sum_l                += buffer_l[i] * sinc_val; | ||||
|             sum_r                += buffer_r[i] * sinc_val; | ||||
|          } | ||||
| 
 | ||||
|          output[0]                = sum_l; | ||||
|          output[1]                = sum_r; | ||||
| 
 | ||||
|          output                  += 2; | ||||
|          out_frames++; | ||||
|          resamp->time            += ratio; | ||||
|       } | ||||
| 
 | ||||
|    } | ||||
| 
 | ||||
|    data->output_frames = out_frames; | ||||
| } | ||||
| 
 | ||||
| static void resampler_sinc_free(void *data) | ||||
| { | ||||
|    rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)data; | ||||
|    if (resamp) | ||||
|       memalign_free(resamp->main_buffer); | ||||
|    free(resamp); | ||||
| } | ||||
| 
 | ||||
| static void sinc_init_table_kaiser(rarch_sinc_resampler_t *resamp, | ||||
|       double cutoff, | ||||
|       float *phase_table, int phases, int taps, bool calculate_delta) | ||||
| { | ||||
|    int i, j; | ||||
|    double    window_mod = kaiser_window_function(0.0, resamp->kaiser_beta); /* Need to normalize w(0) to 1.0. */ | ||||
|    int           stride = calculate_delta ? 2 : 1; | ||||
|    double     sidelobes = taps / 2.0; | ||||
| 
 | ||||
|    for (i = 0; i < phases; i++) | ||||
|    { | ||||
|       for (j = 0; j < taps; j++) | ||||
|       { | ||||
|          double sinc_phase; | ||||
|          float val; | ||||
|          int               n = j * phases + i; | ||||
|          double window_phase = (double)n / (phases * taps); /* [0, 1). */ | ||||
|          window_phase        = 2.0 * window_phase - 1.0; /* [-1, 1) */ | ||||
|          sinc_phase          = sidelobes * window_phase; | ||||
|          val                 = cutoff * sinc(M_PI * sinc_phase * cutoff) * | ||||
|             kaiser_window_function(window_phase, resamp->kaiser_beta) / window_mod; | ||||
|          phase_table[i * stride * taps + j] = val; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    if (calculate_delta) | ||||
|    { | ||||
|       int phase; | ||||
|       int p; | ||||
| 
 | ||||
|       for (p = 0; p < phases - 1; p++) | ||||
|       { | ||||
|          for (j = 0; j < taps; j++) | ||||
|          { | ||||
|             float delta = phase_table[(p + 1) * stride * taps + j] - | ||||
|                phase_table[p * stride * taps + j]; | ||||
|             phase_table[(p * stride + 1) * taps + j] = delta; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       phase = phases - 1; | ||||
|       for (j = 0; j < taps; j++) | ||||
|       { | ||||
|          float val, delta; | ||||
|          double sinc_phase; | ||||
|          int n               = j * phases + (phase + 1); | ||||
|          double window_phase = (double)n / (phases * taps); /* (0, 1]. */ | ||||
|          window_phase        = 2.0 * window_phase - 1.0; /* (-1, 1] */ | ||||
|          sinc_phase          = sidelobes * window_phase; | ||||
| 
 | ||||
|          val                 = cutoff * sinc(M_PI * sinc_phase * cutoff) * | ||||
|             kaiser_window_function(window_phase, resamp->kaiser_beta) / window_mod; | ||||
|          delta = (val - phase_table[phase * stride * taps + j]); | ||||
|          phase_table[(phase * stride + 1) * taps + j] = delta; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void sinc_init_table_lanczos(rarch_sinc_resampler_t *resamp, double cutoff, | ||||
|       float *phase_table, int phases, int taps, bool calculate_delta) | ||||
| { | ||||
|    int i, j; | ||||
|    double    window_mod = lanzcos_window_function(0.0); /* Need to normalize w(0) to 1.0. */ | ||||
|    int           stride = calculate_delta ? 2 : 1; | ||||
|    double     sidelobes = taps / 2.0; | ||||
| 
 | ||||
|    for (i = 0; i < phases; i++) | ||||
|    { | ||||
|       for (j = 0; j < taps; j++) | ||||
|       { | ||||
|          double sinc_phase; | ||||
|          float val; | ||||
|          int               n = j * phases + i; | ||||
|          double window_phase = (double)n / (phases * taps); /* [0, 1). */ | ||||
|          window_phase        = 2.0 * window_phase - 1.0; /* [-1, 1) */ | ||||
|          sinc_phase          = sidelobes * window_phase; | ||||
|          val                 = cutoff * sinc(M_PI * sinc_phase * cutoff) * | ||||
|             lanzcos_window_function(window_phase) / window_mod; | ||||
|          phase_table[i * stride * taps + j] = val; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    if (calculate_delta) | ||||
|    { | ||||
|       int phase; | ||||
|       int p; | ||||
| 
 | ||||
|       for (p = 0; p < phases - 1; p++) | ||||
|       { | ||||
|          for (j = 0; j < taps; j++) | ||||
|          { | ||||
|             float delta = phase_table[(p + 1) * stride * taps + j] - | ||||
|                phase_table[p * stride * taps + j]; | ||||
|             phase_table[(p * stride + 1) * taps + j] = delta; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       phase = phases - 1; | ||||
|       for (j = 0; j < taps; j++) | ||||
|       { | ||||
|          float val, delta; | ||||
|          double sinc_phase; | ||||
|          int n               = j * phases + (phase + 1); | ||||
|          double window_phase = (double)n / (phases * taps); /* (0, 1]. */ | ||||
|          window_phase        = 2.0 * window_phase - 1.0; /* (-1, 1] */ | ||||
|          sinc_phase          = sidelobes * window_phase; | ||||
| 
 | ||||
|          val                 = cutoff * sinc(M_PI * sinc_phase * cutoff) * | ||||
|             lanzcos_window_function(window_phase) / window_mod; | ||||
|          delta = (val - phase_table[phase * stride * taps + j]); | ||||
|          phase_table[(phase * stride + 1) * taps + j] = delta; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *resampler_sinc_new(const struct resampler_config *config, | ||||
|       double bandwidth_mod, enum resampler_quality quality,  | ||||
|       resampler_simd_mask_t mask) | ||||
| { | ||||
|    double cutoff                  = 0.0; | ||||
|    size_t phase_elems             = 0; | ||||
|    size_t elems                   = 0; | ||||
|    unsigned sidelobes             = 0; | ||||
|    rarch_sinc_resampler_t *re     = (rarch_sinc_resampler_t*) | ||||
|       calloc(1, sizeof(*re)); | ||||
| 
 | ||||
|    if (!re) | ||||
|       return NULL; | ||||
| 
 | ||||
|    re->window_type                = SINC_WINDOW_NONE; | ||||
| 
 | ||||
|    switch (quality) | ||||
|    { | ||||
|       case RESAMPLER_QUALITY_LOWEST: | ||||
|          cutoff            = 0.98; | ||||
|          sidelobes         = 2; | ||||
|          re->phase_bits    = 12; | ||||
|          re->subphase_bits = 10; | ||||
|          re->window_type   = SINC_WINDOW_LANCZOS; | ||||
|          re->enable_avx    = 0; | ||||
|          break; | ||||
|       case RESAMPLER_QUALITY_LOWER: | ||||
|          cutoff            = 0.98; | ||||
|          sidelobes         = 4; | ||||
|          re->phase_bits    = 12; | ||||
|          re->subphase_bits = 10; | ||||
|          re->window_type   = SINC_WINDOW_LANCZOS; | ||||
|          re->enable_avx    = 0; | ||||
|          break; | ||||
|       case RESAMPLER_QUALITY_HIGHER: | ||||
|          cutoff            = 0.90; | ||||
|          sidelobes         = 32; | ||||
|          re->phase_bits    = 10; | ||||
|          re->subphase_bits = 14; | ||||
|          re->window_type   = SINC_WINDOW_KAISER; | ||||
|          re->kaiser_beta   = 10.5; | ||||
|          re->enable_avx    = 1; | ||||
|          break; | ||||
|       case RESAMPLER_QUALITY_HIGHEST: | ||||
|          cutoff            = 0.962; | ||||
|          sidelobes         = 128; | ||||
|          re->phase_bits    = 10; | ||||
|          re->subphase_bits = 14; | ||||
|          re->window_type   = SINC_WINDOW_KAISER; | ||||
|          re->kaiser_beta   = 14.5; | ||||
|          re->enable_avx    = 1; | ||||
|          break; | ||||
|       case RESAMPLER_QUALITY_NORMAL: | ||||
|       case RESAMPLER_QUALITY_DONTCARE: | ||||
|          cutoff            = 0.825; | ||||
|          sidelobes         = 8; | ||||
|          re->phase_bits    = 8; | ||||
|          re->subphase_bits = 16; | ||||
|          re->window_type   = SINC_WINDOW_KAISER; | ||||
|          re->kaiser_beta   = 5.5; | ||||
|          re->enable_avx    = 0; | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    re->subphase_mask = (1 << re->subphase_bits) - 1; | ||||
|    re->subphase_mod  = 1.0f / (1 << re->subphase_bits); | ||||
|    re->taps          = sidelobes * 2; | ||||
| 
 | ||||
|    /* Downsampling, must lower cutoff, and extend number of
 | ||||
|     * taps accordingly to keep same stopband attenuation. */ | ||||
|    if (bandwidth_mod < 1.0) | ||||
|    { | ||||
|       cutoff *= bandwidth_mod; | ||||
|       re->taps = (unsigned)ceil(re->taps / bandwidth_mod); | ||||
|    } | ||||
| 
 | ||||
|    /* Be SIMD-friendly. */ | ||||
| #if defined(__AVX__) | ||||
|    if (re->enable_avx) | ||||
|       re->taps  = (re->taps + 7) & ~7; | ||||
|    else | ||||
| #endif | ||||
|    { | ||||
| #if defined(WANT_NEON) | ||||
|       re->taps     = (re->taps + 7) & ~7; | ||||
| #else | ||||
|       re->taps     = (re->taps + 3) & ~3; | ||||
| #endif | ||||
|    } | ||||
| 
 | ||||
|    phase_elems     = ((1 << re->phase_bits) * re->taps); | ||||
|    if (re->window_type == SINC_WINDOW_KAISER) | ||||
|       phase_elems  = phase_elems * 2; | ||||
|    elems           = phase_elems + 4 * re->taps; | ||||
| 
 | ||||
|    re->main_buffer = (float*)memalign_alloc(128, sizeof(float) * elems); | ||||
|    if (!re->main_buffer) | ||||
|       goto error; | ||||
| 
 | ||||
|    re->phase_table = re->main_buffer; | ||||
|    re->buffer_l    = re->main_buffer + phase_elems; | ||||
|    re->buffer_r    = re->buffer_l + 2 * re->taps; | ||||
| 
 | ||||
|    switch (re->window_type) | ||||
|    { | ||||
|       case SINC_WINDOW_LANCZOS: | ||||
|          sinc_init_table_lanczos(re, cutoff, re->phase_table, | ||||
|                1 << re->phase_bits, re->taps, false); | ||||
|          break; | ||||
|       case SINC_WINDOW_KAISER: | ||||
|          sinc_init_table_kaiser(re, cutoff, re->phase_table, | ||||
|                1 << re->phase_bits, re->taps, true); | ||||
|          break; | ||||
|       case SINC_WINDOW_NONE: | ||||
|          goto error; | ||||
|    } | ||||
| 
 | ||||
|    sinc_resampler.process = resampler_sinc_process_c; | ||||
| 
 | ||||
|    if (mask & RESAMPLER_SIMD_AVX && re->enable_avx) | ||||
|    { | ||||
| #if defined(__AVX__) | ||||
|       sinc_resampler.process = resampler_sinc_process_avx; | ||||
| #endif | ||||
|    } | ||||
|    else if (mask & RESAMPLER_SIMD_SSE) | ||||
|    { | ||||
| #if defined(__SSE__) | ||||
|       sinc_resampler.process = resampler_sinc_process_sse; | ||||
| #endif | ||||
|    } | ||||
|    else if (mask & RESAMPLER_SIMD_NEON && re->window_type != SINC_WINDOW_KAISER) | ||||
|    { | ||||
| #if defined(WANT_NEON) | ||||
|       sinc_resampler.process = resampler_sinc_process_neon; | ||||
| #endif | ||||
|    } | ||||
| 
 | ||||
|    return re; | ||||
| 
 | ||||
| error: | ||||
|    resampler_sinc_free(re); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| retro_resampler_t sinc_resampler = { | ||||
|    resampler_sinc_new, | ||||
|    resampler_sinc_process_c, | ||||
|    resampler_sinc_free, | ||||
|    RESAMPLER_API_VERSION, | ||||
|    "sinc", | ||||
|    "sinc" | ||||
| }; | ||||
| 
 | ||||
| #undef WANT_NEON | ||||
|  | @ -0,0 +1,74 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (sinc_resampler_neon.S). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #if defined(__ARM_NEON__) | ||||
| 
 | ||||
| #ifndef __MACH__ | ||||
| .arm | ||||
| #endif | ||||
| .align 4
 | ||||
| .globl process_sinc_neon_asm
 | ||||
| #ifndef __MACH__ | ||||
| .type process_sinc_neon_asm, %function | ||||
| #endif | ||||
| .globl _process_sinc_neon_asm
 | ||||
| #ifndef __MACH__ | ||||
| .type _process_sinc_neon_asm, %function | ||||
| #endif | ||||
| # void process_sinc_neon(float *out, const float *left, const float *right, const float *coeff, unsigned taps) | ||||
| # Assumes taps is >= 8, and a multiple of 8. | ||||
| process_sinc_neon_asm: | ||||
| _process_sinc_neon_asm: | ||||
| 
 | ||||
|    push {r4, lr}    | ||||
|    vmov.f32 q0, #0.0 | ||||
|    vmov.f32 q8, #0.0 | ||||
| 
 | ||||
|    # Taps argument (r4) goes on stack in armeabi. | ||||
|    ldr r4, [sp, #8] | ||||
| 
 | ||||
| 1: | ||||
|    # Left | ||||
|    vld1.f32 {q2-q3}, [r1]! | ||||
|    # Right | ||||
|    vld1.f32 {q10-q11}, [r2]! | ||||
|    # Coeff | ||||
|    vld1.f32 {q12-q13}, [r3, :128]! | ||||
| 
 | ||||
|    # Left / Right | ||||
|    vmla.f32 q0, q2, q12 | ||||
|    vmla.f32 q8, q10, q12 | ||||
|    vmla.f32 q0, q3, q13 | ||||
|    vmla.f32 q8, q11, q13 | ||||
| 
 | ||||
|    subs r4, r4, #8 | ||||
|    bne 1b | ||||
| 
 | ||||
|    # Add everything together | ||||
|    vadd.f32 d0, d0, d1 | ||||
|    vadd.f32 d16, d16, d17 | ||||
|    vpadd.f32 d0, d0, d16 | ||||
|    vst1.f32 d0, [r0] | ||||
|     | ||||
|    pop {r4, pc} | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,159 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (compat_fnmatch.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #if __TEST_FNMATCH__ | ||||
| #include <assert.h> | ||||
| #endif | ||||
| #include <stddef.h> | ||||
| 
 | ||||
| #include <compat/fnmatch.h> | ||||
| 
 | ||||
| /* Implemnentation of fnmatch(3) so it can be
 | ||||
|  * distributed to non *nix platforms. | ||||
|  * | ||||
|  * No flags are implemented ATM. | ||||
|  * We don't use them. Add flags as needed. */ | ||||
| 
 | ||||
| int rl_fnmatch(const char *pattern, const char *string, int flags) | ||||
| { | ||||
|    int rv; | ||||
|    const char *c = NULL; | ||||
|    int charmatch = 0; | ||||
| 
 | ||||
|    for (c = pattern; *c != '\0'; c++) | ||||
|    { | ||||
|       /* String ended before pattern */ | ||||
|       if ((*c != '*') && (*string == '\0')) | ||||
|          return FNM_NOMATCH; | ||||
| 
 | ||||
|       switch (*c) | ||||
|       { | ||||
|          /* Match any number of unknown chars */ | ||||
|          case '*': | ||||
|             /* Find next node in the pattern
 | ||||
|              * ignoring multiple asterixes | ||||
|              */ | ||||
|             do { | ||||
|                c++; | ||||
|                if (*c == '\0') | ||||
|                   return 0; | ||||
|             } while (*c == '*'); | ||||
| 
 | ||||
|             /* Match the remaining pattern
 | ||||
|              * ignoring more and more characters. */ | ||||
|             do { | ||||
|                /* We reached the end of the string without a
 | ||||
|                 * match. There is a way to optimize this by | ||||
|                 * calculating the minimum chars needed to | ||||
|                 * match the remaining pattern but I don't | ||||
|                 * think it is worth the work ATM. | ||||
|                 */ | ||||
|                if (*string == '\0') | ||||
|                   return FNM_NOMATCH; | ||||
| 
 | ||||
|                rv = rl_fnmatch(c, string, flags); | ||||
|                string++; | ||||
|             } while (rv != 0); | ||||
| 
 | ||||
|             return 0; | ||||
|             /* Match char from list */ | ||||
|          case '[': | ||||
|             charmatch = 0; | ||||
|             for (c++; *c != ']'; c++) | ||||
|             { | ||||
|                /* Bad format */ | ||||
|                if (*c == '\0') | ||||
|                   return FNM_NOMATCH; | ||||
| 
 | ||||
|                /* Match already found */ | ||||
|                if (charmatch) | ||||
|                   continue; | ||||
| 
 | ||||
|                if (*c == *string) | ||||
|                   charmatch = 1; | ||||
|             } | ||||
| 
 | ||||
|             /* No match in list */ | ||||
|             if (!charmatch) | ||||
|                return FNM_NOMATCH; | ||||
| 
 | ||||
|             string++; | ||||
|             break; | ||||
|             /* Has any character */ | ||||
|          case '?': | ||||
|             string++; | ||||
|             break; | ||||
|             /* Match following character verbatim */ | ||||
|          case '\\': | ||||
|             c++; | ||||
|             /* Dangling escape at end of pattern.
 | ||||
|              * FIXME: Was c == '\0' (makes no sense). | ||||
|              * Not sure if c == NULL or *c == '\0' | ||||
|              * is intended. Assuming *c due to c++ right before. */ | ||||
|             if (*c == '\0') | ||||
|                return FNM_NOMATCH; | ||||
|          default: | ||||
|             if (*c != *string) | ||||
|                return FNM_NOMATCH; | ||||
|             string++; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    /* End of string and end of pattend */ | ||||
|    if (*string == '\0') | ||||
|       return 0; | ||||
|    return FNM_NOMATCH; | ||||
| } | ||||
| 
 | ||||
| #if __TEST_FNMATCH__ | ||||
| int main(void) | ||||
| { | ||||
|    assert(rl_fnmatch("TEST", "TEST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE?T", "TEST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE[Ssa]T", "TEST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE[Ssda]T", "TEsT", 0) == 0); | ||||
|    assert(rl_fnmatch("TE[Ssda]T", "TEdT", 0) == 0); | ||||
|    assert(rl_fnmatch("TE[Ssda]T", "TEaT", 0) == 0); | ||||
|    assert(rl_fnmatch("TEST*", "TEST", 0) == 0); | ||||
|    assert(rl_fnmatch("TEST**", "TEST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE*ST*", "TEST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE**ST*", "TEST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE**ST*", "TExST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE**ST", "TEST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE**ST", "TExST", 0) == 0); | ||||
|    assert(rl_fnmatch("TE\\**ST", "TE*xST", 0) == 0); | ||||
|    assert(rl_fnmatch("*.*", "test.jpg", 0) == 0); | ||||
|    assert(rl_fnmatch("*.jpg", "test.jpg", 0) == 0); | ||||
|    assert(rl_fnmatch("*.[Jj][Pp][Gg]", "test.jPg", 0) == 0); | ||||
|    assert(rl_fnmatch("*.[Jj]*[Gg]", "test.jPg", 0) == 0); | ||||
|    assert(rl_fnmatch("TEST?", "TEST", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("TES[asd", "TEST", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("TEST\\", "TEST", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("TEST*S", "TEST", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("TE**ST", "TExT", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("TE\\*T", "TExT", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("TES?", "TES", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("TE", "TEST", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("TEST!", "TEST", 0) == FNM_NOMATCH); | ||||
|    assert(rl_fnmatch("DSAD", "TEST", 0) == FNM_NOMATCH); | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,219 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (compat_getopt.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| #include <string.h> | ||||
| #include <boolean.h> | ||||
| #include <stddef.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <retro_miscellaneous.h> | ||||
| 
 | ||||
| #include <compat/getopt.h> | ||||
| #include <compat/strl.h> | ||||
| #include <compat/strcasestr.h> | ||||
| #include <compat/posix_string.h> | ||||
| 
 | ||||
| #include <retro_assert.h> | ||||
| 
 | ||||
| char *optarg; | ||||
| int optind, opterr, optopt; | ||||
| 
 | ||||
| static bool is_short_option(const char *str) | ||||
| { | ||||
|    return str[0] == '-' && str[1] != '-'; | ||||
| } | ||||
| 
 | ||||
| static bool is_long_option(const char *str) | ||||
| { | ||||
|    return str[0] == '-' && str[1] == '-'; | ||||
| } | ||||
| 
 | ||||
| static int find_short_index(char * const *argv) | ||||
| { | ||||
|    int idx; | ||||
|    for (idx = 0; argv[idx]; idx++) | ||||
|    { | ||||
|       if (is_short_option(argv[idx])) | ||||
|          return idx; | ||||
|    } | ||||
| 
 | ||||
|    return -1; | ||||
| } | ||||
| 
 | ||||
| static int find_long_index(char * const *argv) | ||||
| { | ||||
|    int idx; | ||||
|    for (idx = 0; argv[idx]; idx++) | ||||
|    { | ||||
|       if (is_long_option(argv[idx])) | ||||
|          return idx; | ||||
|    } | ||||
| 
 | ||||
|    return -1; | ||||
| } | ||||
| 
 | ||||
| static int parse_short(const char *optstring, char * const *argv) | ||||
| { | ||||
|    bool extra_opt, takes_arg, embedded_arg; | ||||
|    const char *opt = NULL; | ||||
|    char        arg = argv[0][1]; | ||||
| 
 | ||||
|    if (arg == ':') | ||||
|       return '?'; | ||||
| 
 | ||||
|    opt = strchr(optstring, arg); | ||||
|    if (!opt) | ||||
|       return '?'; | ||||
| 
 | ||||
|    extra_opt = argv[0][2]; | ||||
|    takes_arg = opt[1] == ':'; | ||||
| 
 | ||||
|    /* If we take an argument, and we see additional characters,
 | ||||
|     * this is in fact the argument (i.e. -cfoo is same as -c foo). */ | ||||
|    embedded_arg = extra_opt && takes_arg; | ||||
| 
 | ||||
|    if (takes_arg) | ||||
|    { | ||||
|       if (embedded_arg) | ||||
|       { | ||||
|          optarg = argv[0] + 2; | ||||
|          optind++; | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|          optarg = argv[1]; | ||||
|          optind += 2; | ||||
|       } | ||||
| 
 | ||||
|       return optarg ? opt[0] : '?'; | ||||
|    } | ||||
| 
 | ||||
|    if (embedded_arg) | ||||
|    { | ||||
|       /* If we see additional characters,
 | ||||
|        * and they don't take arguments, this | ||||
|        * means we have multiple flags in one. */ | ||||
|       memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1); | ||||
|       return opt[0]; | ||||
|    } | ||||
| 
 | ||||
|    optind++; | ||||
|    return opt[0]; | ||||
| } | ||||
| 
 | ||||
| static int parse_long(const struct option *longopts, char * const *argv) | ||||
| { | ||||
|    size_t indice; | ||||
|    const struct option *opt = NULL; | ||||
|    for (indice = 0; longopts[indice].name; indice++) | ||||
|    { | ||||
|       if (!strcmp(longopts[indice].name, &argv[0][2])) | ||||
|       { | ||||
|          opt = &longopts[indice]; | ||||
|          break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    if (!opt) | ||||
|       return '?'; | ||||
| 
 | ||||
|    /* getopt_long has an "optional" arg, but we don't bother with that. */ | ||||
|    if (opt->has_arg && !argv[1]) | ||||
|       return '?'; | ||||
| 
 | ||||
|    if (opt->has_arg) | ||||
|    { | ||||
|       optarg = argv[1]; | ||||
|       optind += 2; | ||||
|    } | ||||
|    else | ||||
|       optind++; | ||||
| 
 | ||||
|    if (opt->flag) | ||||
|    { | ||||
|       *opt->flag = opt->val; | ||||
|       return 0; | ||||
|    } | ||||
| 
 | ||||
|    return opt->val; | ||||
| } | ||||
| 
 | ||||
| static void shuffle_block(char **begin, char **last, char **end) | ||||
| { | ||||
|    ptrdiff_t    len = last - begin; | ||||
|    const char **tmp = (const char**)calloc(len, sizeof(const char*)); | ||||
| 
 | ||||
|    retro_assert(tmp); | ||||
| 
 | ||||
|    memcpy((void*)tmp, begin, len * sizeof(const char*)); | ||||
|    memmove(begin, last, (end - last) * sizeof(const char*)); | ||||
|    memcpy(end - len, tmp, len * sizeof(const char*)); | ||||
| 
 | ||||
|    free((void*)tmp); | ||||
| } | ||||
| 
 | ||||
| int getopt_long(int argc, char *argv[], | ||||
|       const char *optstring, const struct option *longopts, int *longindex) | ||||
| { | ||||
|    int short_index, long_index; | ||||
| 
 | ||||
|    (void)longindex; | ||||
| 
 | ||||
|    if (optind == 0) | ||||
|       optind = 1; | ||||
| 
 | ||||
|    if (argc < 2) | ||||
|       return -1; | ||||
| 
 | ||||
|    short_index = find_short_index(&argv[optind]); | ||||
|    long_index  = find_long_index(&argv[optind]); | ||||
| 
 | ||||
|    /* We're done here. */ | ||||
|    if (short_index == -1 && long_index == -1) | ||||
|       return -1; | ||||
| 
 | ||||
|    /* Reorder argv so that non-options come last.
 | ||||
|     * Non-POSIXy, but that's what getopt does by default. */ | ||||
|    if ((short_index > 0) && ((short_index < long_index) || (long_index == -1))) | ||||
|    { | ||||
|       shuffle_block(&argv[optind], &argv[optind + short_index], &argv[argc]); | ||||
|       short_index = 0; | ||||
|    } | ||||
|    else if ((long_index > 0) && ((long_index < short_index) | ||||
|             || (short_index == -1))) | ||||
|    { | ||||
|       shuffle_block(&argv[optind], &argv[optind + long_index], &argv[argc]); | ||||
|       long_index = 0; | ||||
|    } | ||||
| 
 | ||||
|    retro_assert(short_index == 0 || long_index == 0); | ||||
| 
 | ||||
|    if (short_index == 0) | ||||
|       return parse_short(optstring, &argv[optind]); | ||||
|    if (long_index == 0) | ||||
|       return parse_long(longopts, &argv[optind]); | ||||
| 
 | ||||
|    return '?'; | ||||
| } | ||||
|  | @ -0,0 +1,619 @@ | |||
| /*
 | ||||
| Copyright (c) 2013, Kenneth MacKay | ||||
| All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without modification, | ||||
| are permitted provided that the following conditions are met: | ||||
|  * Redistributions of source code must retain the above copyright notice, this | ||||
|    list of conditions and the following disclaimer. | ||||
|  * Redistributions in binary form must reproduce the above copyright notice, | ||||
|    this list of conditions and the following disclaimer in the documentation | ||||
|    and/or other materials provided with the distribution. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||||
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| */ | ||||
| 
 | ||||
| #include <compat/ifaddrs.h> | ||||
| 
 | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <stddef.h> | ||||
| #include <errno.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netpacket/packet.h> | ||||
| #include <net/if_arp.h> | ||||
| #include <netinet/in.h> | ||||
| #include <linux/netlink.h> | ||||
| #include <linux/rtnetlink.h> | ||||
| 
 | ||||
| typedef struct NetlinkList | ||||
| { | ||||
|     struct NetlinkList *m_next; | ||||
|     struct nlmsghdr *m_data; | ||||
|     unsigned int m_size; | ||||
| } NetlinkList; | ||||
| 
 | ||||
| static int netlink_socket(void) | ||||
| { | ||||
|    struct sockaddr_nl l_addr; | ||||
|    int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | ||||
| 
 | ||||
|    if(l_socket < 0) | ||||
|       return -1; | ||||
| 
 | ||||
|    memset(&l_addr, 0, sizeof(l_addr)); | ||||
|    l_addr.nl_family = AF_NETLINK; | ||||
|    if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0) | ||||
|    { | ||||
|       close(l_socket); | ||||
|       return -1; | ||||
|    } | ||||
| 
 | ||||
|    return l_socket; | ||||
| } | ||||
| 
 | ||||
| static int netlink_send(int p_socket, int p_request) | ||||
| { | ||||
|    struct | ||||
|    { | ||||
|       struct nlmsghdr m_hdr; | ||||
|       struct rtgenmsg m_msg; | ||||
|    } l_data; | ||||
|    struct sockaddr_nl l_addr; | ||||
| 
 | ||||
|    memset(&l_data, 0, sizeof(l_data)); | ||||
| 
 | ||||
|    l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); | ||||
|    l_data.m_hdr.nlmsg_type = p_request; | ||||
|    l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; | ||||
|    l_data.m_hdr.nlmsg_pid = 0; | ||||
|    l_data.m_hdr.nlmsg_seq = p_socket; | ||||
|    l_data.m_msg.rtgen_family = AF_UNSPEC; | ||||
| 
 | ||||
|    memset(&l_addr, 0, sizeof(l_addr)); | ||||
|    l_addr.nl_family = AF_NETLINK; | ||||
|    return (sendto(p_socket, &l_data.m_hdr, l_data.m_hdr.nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr))); | ||||
| } | ||||
| 
 | ||||
| static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) | ||||
| { | ||||
|    struct msghdr l_msg; | ||||
|    struct iovec l_iov = { p_buffer, p_len }; | ||||
|    struct sockaddr_nl l_addr; | ||||
| 
 | ||||
|    for(;;) | ||||
|    { | ||||
|       l_msg.msg_name = (void *)&l_addr; | ||||
|       l_msg.msg_namelen = sizeof(l_addr); | ||||
|       l_msg.msg_iov = &l_iov; | ||||
|       l_msg.msg_iovlen = 1; | ||||
|       l_msg.msg_control = NULL; | ||||
|       l_msg.msg_controllen = 0; | ||||
|       l_msg.msg_flags = 0; | ||||
|       int l_result = recvmsg(p_socket, &l_msg, 0); | ||||
| 
 | ||||
|       if(l_result < 0) | ||||
|       { | ||||
|          if(errno == EINTR) | ||||
|             continue; | ||||
|          return -2; | ||||
|       } | ||||
| 
 | ||||
|       if(l_msg.msg_flags & MSG_TRUNC) /* buffer too small */ | ||||
|          return -1; | ||||
|       return l_result; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done) | ||||
| { | ||||
|    size_t l_size = 4096; | ||||
|    void *l_buffer = NULL; | ||||
| 
 | ||||
|    for(;;) | ||||
|    { | ||||
|       free(l_buffer); | ||||
|       l_buffer = malloc(l_size); | ||||
|       if (l_buffer == NULL) | ||||
|          return NULL; | ||||
| 
 | ||||
|       int l_read = netlink_recv(p_socket, l_buffer, l_size); | ||||
|       *p_size = l_read; | ||||
| 
 | ||||
|       if(l_read == -2) | ||||
|       { | ||||
|          free(l_buffer); | ||||
|          return NULL; | ||||
|       } | ||||
| 
 | ||||
|       if(l_read >= 0) | ||||
|       { | ||||
|          pid_t l_pid = getpid(); | ||||
|          struct nlmsghdr *l_hdr; | ||||
|          for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read)) | ||||
|          { | ||||
|             if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) | ||||
|                continue; | ||||
| 
 | ||||
|             if(l_hdr->nlmsg_type == NLMSG_DONE) | ||||
|             { | ||||
|                *p_done = 1; | ||||
|                break; | ||||
|             } | ||||
| 
 | ||||
|             if(l_hdr->nlmsg_type == NLMSG_ERROR) | ||||
|             { | ||||
|                free(l_buffer); | ||||
|                return NULL; | ||||
|             } | ||||
|          } | ||||
|          return l_buffer; | ||||
|       } | ||||
| 
 | ||||
|       l_size *= 2; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size) | ||||
| { | ||||
|    NetlinkList *l_item = malloc(sizeof(NetlinkList)); | ||||
|    if (l_item == NULL) | ||||
|       return NULL; | ||||
| 
 | ||||
|    l_item->m_next = NULL; | ||||
|    l_item->m_data = p_data; | ||||
|    l_item->m_size = p_size; | ||||
|    return l_item; | ||||
| } | ||||
| 
 | ||||
| static void freeResultList(NetlinkList *p_list) | ||||
| { | ||||
|    NetlinkList *l_cur; | ||||
|    while(p_list) | ||||
|    { | ||||
|       l_cur = p_list; | ||||
|       p_list = p_list->m_next; | ||||
|       free(l_cur->m_data); | ||||
|       free(l_cur); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static NetlinkList *getResultList(int p_socket, int p_request) | ||||
| { | ||||
|    if(netlink_send(p_socket, p_request) < 0) | ||||
|       return NULL; | ||||
| 
 | ||||
|    NetlinkList *l_list = NULL; | ||||
|    NetlinkList *l_end = NULL; | ||||
|    int l_size; | ||||
|    int l_done = 0; | ||||
|    while(!l_done) | ||||
|    { | ||||
|       NetlinkList *l_item    = NULL; | ||||
|       struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done); | ||||
|       if(!l_hdr) | ||||
|          goto error; | ||||
| 
 | ||||
|       l_item = newListItem(l_hdr, l_size); | ||||
|       if (!l_item) | ||||
|          goto error; | ||||
| 
 | ||||
|       if(!l_list) | ||||
|          l_list = l_item; | ||||
|       else | ||||
|          l_end->m_next = l_item; | ||||
|       l_end = l_item; | ||||
|    } | ||||
| 
 | ||||
|    return l_list; | ||||
| 
 | ||||
| error: | ||||
|    freeResultList(l_list); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static size_t maxSize(size_t a, size_t b) | ||||
| { | ||||
|    return (a > b ? a : b); | ||||
| } | ||||
| 
 | ||||
| static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) | ||||
| { | ||||
|    switch(p_family) | ||||
|    { | ||||
|       case AF_INET: | ||||
|          return sizeof(struct sockaddr_in); | ||||
|       case AF_INET6: | ||||
|          return sizeof(struct sockaddr_in6); | ||||
|       case AF_PACKET: | ||||
|          return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); | ||||
|       default: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); | ||||
| } | ||||
| 
 | ||||
| static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size) | ||||
| { | ||||
|    switch(p_family) | ||||
|    { | ||||
|       case AF_INET: | ||||
|          memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size); | ||||
|          break; | ||||
|       case AF_INET6: | ||||
|          memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size); | ||||
|          break; | ||||
|       case AF_PACKET: | ||||
|          memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size); | ||||
|          ((struct sockaddr_ll*)p_dest)->sll_halen = p_size; | ||||
|          break; | ||||
|       default: | ||||
|          memcpy(p_dest->sa_data, p_data, p_size); | ||||
|          break; | ||||
|    } | ||||
|    p_dest->sa_family = p_family; | ||||
| } | ||||
| 
 | ||||
| static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry) | ||||
| { | ||||
|    if(!*p_resultList) | ||||
|       *p_resultList = p_entry; | ||||
|    else | ||||
|    { | ||||
|       struct ifaddrs *l_cur = *p_resultList; | ||||
|       while(l_cur->ifa_next) | ||||
|          l_cur = l_cur->ifa_next; | ||||
|       l_cur->ifa_next = p_entry; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) | ||||
| { | ||||
|    struct ifaddrs *l_entry  = NULL; | ||||
|    struct rtattr *l_rta     = NULL; | ||||
|    struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr); | ||||
|    size_t l_nameSize        = 0; | ||||
|    size_t l_addrSize        = 0; | ||||
|    size_t l_dataSize        = 0; | ||||
|    size_t l_rtaSize         = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); | ||||
| 
 | ||||
|    for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) | ||||
|    { | ||||
|       size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); | ||||
|       switch(l_rta->rta_type) | ||||
|       { | ||||
|          case IFLA_ADDRESS: | ||||
|          case IFLA_BROADCAST: | ||||
|             l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize)); | ||||
|             break; | ||||
|          case IFLA_IFNAME: | ||||
|             l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); | ||||
|             break; | ||||
|          case IFLA_STATS: | ||||
|             l_dataSize += NLMSG_ALIGN(l_rtaSize); | ||||
|             break; | ||||
|          default: | ||||
|             break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    l_entry = malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize); | ||||
|    if (l_entry == NULL) | ||||
|       return -1; | ||||
| 
 | ||||
|    memset(l_entry, 0, sizeof(struct ifaddrs)); | ||||
|    l_entry->ifa_name = ""; | ||||
| 
 | ||||
|    char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs); | ||||
|    char *l_name = l_index + sizeof(int); | ||||
|    char *l_addr = l_name + l_nameSize; | ||||
|    char *l_data = l_addr + l_addrSize; | ||||
| 
 | ||||
|    // save the interface index so we can look it up when handling the addresses.
 | ||||
|    memcpy(l_index, &l_info->ifi_index, sizeof(int)); | ||||
| 
 | ||||
|    l_entry->ifa_flags = l_info->ifi_flags; | ||||
| 
 | ||||
|    l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); | ||||
|    for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) | ||||
|    { | ||||
|       void *l_rtaData = RTA_DATA(l_rta); | ||||
|       size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); | ||||
|       switch(l_rta->rta_type) | ||||
|       { | ||||
|          case IFLA_ADDRESS: | ||||
|          case IFLA_BROADCAST: | ||||
|             { | ||||
|                size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize); | ||||
|                makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); | ||||
|                ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index; | ||||
|                ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type; | ||||
|                if(l_rta->rta_type == IFLA_ADDRESS) | ||||
|                   l_entry->ifa_addr = (struct sockaddr *)l_addr; | ||||
|                else | ||||
|                   l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; | ||||
|                l_addr += NLMSG_ALIGN(l_addrLen); | ||||
|                break; | ||||
|             } | ||||
|          case IFLA_IFNAME: | ||||
|             strncpy(l_name, l_rtaData, l_rtaDataSize); | ||||
|             l_name[l_rtaDataSize] = '\0'; | ||||
|             l_entry->ifa_name = l_name; | ||||
|             break; | ||||
|          case IFLA_STATS: | ||||
|             memcpy(l_data, l_rtaData, l_rtaDataSize); | ||||
|             l_entry->ifa_data = l_data; | ||||
|             break; | ||||
|          default: | ||||
|             break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    addToEnd(p_resultList, l_entry); | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks) | ||||
| { | ||||
|    int l_num = 0; | ||||
|    struct ifaddrs *l_cur = *p_links; | ||||
|    while(l_cur && l_num < p_numLinks) | ||||
|    { | ||||
|       int l_index; | ||||
|       char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs); | ||||
| 
 | ||||
|       memcpy(&l_index, l_indexPtr, sizeof(int)); | ||||
|       if(l_index == p_index) | ||||
|          return l_cur; | ||||
| 
 | ||||
|       l_cur = l_cur->ifa_next; | ||||
|       ++l_num; | ||||
|    } | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks) | ||||
| { | ||||
|    size_t l_nameSize = 0; | ||||
|    size_t l_addrSize = 0; | ||||
|    int l_addedNetmask = 0; | ||||
|    struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr); | ||||
|    struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks); | ||||
| 
 | ||||
|    if(l_info->ifa_family == AF_PACKET) | ||||
|       return 0; | ||||
| 
 | ||||
|    size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); | ||||
|    struct rtattr *l_rta; | ||||
| 
 | ||||
|    for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) | ||||
|    { | ||||
|       size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); | ||||
| 
 | ||||
|       switch(l_rta->rta_type) | ||||
|       { | ||||
|          case IFA_ADDRESS: | ||||
|          case IFA_LOCAL: | ||||
|             if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask) | ||||
|             { | ||||
|                /* make room for netmask */ | ||||
|                l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); | ||||
|                l_addedNetmask = 1; | ||||
|             } | ||||
|          case IFA_BROADCAST: | ||||
|             l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); | ||||
|             break; | ||||
|          case IFA_LABEL: | ||||
|             l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); | ||||
|             break; | ||||
|          default: | ||||
|             break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); | ||||
|    if (l_entry == NULL) | ||||
|       return -1; | ||||
| 
 | ||||
|    memset(l_entry, 0, sizeof(struct ifaddrs)); | ||||
|    l_entry->ifa_name = (l_interface ? l_interface->ifa_name : ""); | ||||
| 
 | ||||
|    char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs); | ||||
|    char *l_addr = l_name + l_nameSize; | ||||
| 
 | ||||
|    l_entry->ifa_flags = l_info->ifa_flags; | ||||
|    if(l_interface) | ||||
|       l_entry->ifa_flags |= l_interface->ifa_flags; | ||||
| 
 | ||||
|    l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); | ||||
|    for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) | ||||
|    { | ||||
|       void *l_rtaData = RTA_DATA(l_rta); | ||||
|       size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); | ||||
|       switch(l_rta->rta_type) | ||||
|       { | ||||
|          case IFA_ADDRESS: | ||||
|          case IFA_BROADCAST: | ||||
|          case IFA_LOCAL: | ||||
|             { | ||||
|                size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize); | ||||
|                makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); | ||||
|                if(l_info->ifa_family == AF_INET6) | ||||
|                { | ||||
|                   if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData)) | ||||
|                      ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index; | ||||
|                } | ||||
| 
 | ||||
|                if(l_rta->rta_type == IFA_ADDRESS) | ||||
|                { | ||||
|                   /* apparently in a point-to-point network IFA_ADDRESS
 | ||||
|                    * contains the dest address and IFA_LOCAL contains the local address */ | ||||
|                   if(l_entry->ifa_addr) | ||||
|                      l_entry->ifa_dstaddr = (struct sockaddr *)l_addr; | ||||
|                   else | ||||
|                      l_entry->ifa_addr = (struct sockaddr *)l_addr; | ||||
|                } | ||||
|                else if(l_rta->rta_type == IFA_LOCAL) | ||||
|                { | ||||
|                   if(l_entry->ifa_addr) | ||||
|                      l_entry->ifa_dstaddr = l_entry->ifa_addr; | ||||
|                   l_entry->ifa_addr = (struct sockaddr *)l_addr; | ||||
|                } | ||||
|                else | ||||
|                   l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; | ||||
|                l_addr += NLMSG_ALIGN(l_addrLen); | ||||
|                break; | ||||
|             } | ||||
|          case IFA_LABEL: | ||||
|             strncpy(l_name, l_rtaData, l_rtaDataSize); | ||||
|             l_name[l_rtaDataSize] = '\0'; | ||||
|             l_entry->ifa_name = l_name; | ||||
|             break; | ||||
|          default: | ||||
|             break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    if(l_entry->ifa_addr && | ||||
|          (   l_entry->ifa_addr->sa_family == AF_INET | ||||
|           || l_entry->ifa_addr->sa_family == AF_INET6)) | ||||
|    { | ||||
|       unsigned i; | ||||
|       char l_mask[16]; | ||||
|       unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET | ||||
|             ? 32 : 128); | ||||
|       unsigned l_prefix    = (l_info->ifa_prefixlen > l_maxPrefix | ||||
|             ? l_maxPrefix : l_info->ifa_prefixlen); | ||||
| 
 | ||||
|       l_mask[0] = '\0'; | ||||
| 
 | ||||
|       for(i=0; i<(l_prefix/8); ++i) | ||||
|          l_mask[i] = 0xff; | ||||
|       if(l_prefix % 8) | ||||
|          l_mask[i] = 0xff << (8 - (l_prefix % 8)); | ||||
| 
 | ||||
|       makeSockaddr(l_entry->ifa_addr->sa_family, | ||||
|             (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8); | ||||
|       l_entry->ifa_netmask = (struct sockaddr *)l_addr; | ||||
|    } | ||||
| 
 | ||||
|    addToEnd(p_resultList, l_entry); | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, | ||||
|       struct ifaddrs **p_resultList) | ||||
| { | ||||
|    int l_numLinks = 0; | ||||
|    pid_t l_pid = getpid(); | ||||
|    for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) | ||||
|    { | ||||
|       struct nlmsghdr *l_hdr = NULL; | ||||
|       unsigned int l_nlsize  = p_netlinkList->m_size; | ||||
| 
 | ||||
|       for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) | ||||
|       { | ||||
|          if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) | ||||
|             continue; | ||||
| 
 | ||||
|          if(l_hdr->nlmsg_type == NLMSG_DONE) | ||||
|             break; | ||||
| 
 | ||||
|          if(l_hdr->nlmsg_type == RTM_NEWLINK) | ||||
|          { | ||||
|             if(interpretLink(l_hdr, p_resultList) == -1) | ||||
|                return -1; | ||||
|             ++l_numLinks; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
|    return l_numLinks; | ||||
| } | ||||
| 
 | ||||
| static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, | ||||
|       struct ifaddrs **p_resultList, int p_numLinks) | ||||
| { | ||||
|    pid_t l_pid = getpid(); | ||||
|    for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) | ||||
|    { | ||||
|       struct nlmsghdr *l_hdr = NULL; | ||||
|       unsigned int l_nlsize  = p_netlinkList->m_size; | ||||
| 
 | ||||
|       for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) | ||||
|       { | ||||
|          if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) | ||||
|             continue; | ||||
| 
 | ||||
|          if(l_hdr->nlmsg_type == NLMSG_DONE) | ||||
|             break; | ||||
| 
 | ||||
|          if(l_hdr->nlmsg_type == RTM_NEWADDR) | ||||
|          { | ||||
|             if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1) | ||||
|                return -1; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| int getifaddrs(struct ifaddrs **ifap) | ||||
| { | ||||
|    int l_socket = 0; | ||||
|    int l_result = 0; | ||||
|    if(!ifap) | ||||
|       return -1; | ||||
|    *ifap = NULL; | ||||
| 
 | ||||
|    l_socket = netlink_socket(); | ||||
|    if(l_socket < 0) | ||||
|       return -1; | ||||
| 
 | ||||
|    NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK); | ||||
|    if(!l_linkResults) | ||||
|    { | ||||
|       close(l_socket); | ||||
|       return -1; | ||||
|    } | ||||
| 
 | ||||
|    NetlinkList *l_addrResults = getResultList(l_socket, RTM_GETADDR); | ||||
|    if(!l_addrResults) | ||||
|    { | ||||
|       close(l_socket); | ||||
|       freeResultList(l_linkResults); | ||||
|       return -1; | ||||
|    } | ||||
| 
 | ||||
|    int l_numLinks = interpretLinks(l_socket, l_linkResults, ifap); | ||||
| 
 | ||||
|    if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) | ||||
|       l_result = -1; | ||||
| 
 | ||||
|    freeResultList(l_linkResults); | ||||
|    freeResultList(l_addrResults); | ||||
|    close(l_socket); | ||||
|    return l_result; | ||||
| } | ||||
| 
 | ||||
| void freeifaddrs(struct ifaddrs *ifa) | ||||
| { | ||||
|    struct ifaddrs *l_cur = NULL; | ||||
| 
 | ||||
|    while(ifa) | ||||
|    { | ||||
|       l_cur = ifa; | ||||
|       ifa = ifa->ifa_next; | ||||
|       free(l_cur); | ||||
|    } | ||||
| } | ||||
|  | @ -0,0 +1,104 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (compat_posix_string.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| #include <compat/posix_string.h> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| 
 | ||||
| #undef strcasecmp | ||||
| #undef strdup | ||||
| #undef isblank | ||||
| #undef strtok_r | ||||
| #include <ctype.h> | ||||
| #include <stdlib.h> | ||||
| #include <stddef.h> | ||||
| #include <compat/strl.h> | ||||
| 
 | ||||
| #include <string.h> | ||||
| 
 | ||||
| int retro_strcasecmp__(const char *a, const char *b) | ||||
| { | ||||
|    while (*a && *b) | ||||
|    { | ||||
|       int a_ = tolower(*a); | ||||
|       int b_ = tolower(*b); | ||||
| 
 | ||||
|       if (a_ != b_) | ||||
|          return a_ - b_; | ||||
| 
 | ||||
|       a++; | ||||
|       b++; | ||||
|    } | ||||
| 
 | ||||
|    return tolower(*a) - tolower(*b); | ||||
| } | ||||
| 
 | ||||
| char *retro_strdup__(const char *orig) | ||||
| { | ||||
|    size_t len = strlen(orig) + 1; | ||||
|    char *ret  = (char*)malloc(len); | ||||
|    if (!ret) | ||||
|       return NULL; | ||||
| 
 | ||||
|    strlcpy(ret, orig, len); | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| int retro_isblank__(int c) | ||||
| { | ||||
|    return (c == ' ') || (c == '\t'); | ||||
| } | ||||
| 
 | ||||
| char *retro_strtok_r__(char *str, const char *delim, char **saveptr) | ||||
| { | ||||
|    char *first = NULL; | ||||
|    if (!saveptr || !delim) | ||||
|       return NULL; | ||||
| 
 | ||||
|    if (str) | ||||
|       *saveptr = str; | ||||
| 
 | ||||
|    do | ||||
|    { | ||||
|       char *ptr = NULL; | ||||
|       first = *saveptr; | ||||
|       while (*first && strchr(delim, *first)) | ||||
|          *first++ = '\0'; | ||||
| 
 | ||||
|       if (*first == '\0') | ||||
|          return NULL; | ||||
| 
 | ||||
|       ptr = first + 1; | ||||
| 
 | ||||
|       while (*ptr && !strchr(delim, *ptr)) | ||||
|          ptr++; | ||||
| 
 | ||||
|       *saveptr = ptr + (*ptr ? 1 : 0); | ||||
|       *ptr     = '\0'; | ||||
|    } while (strlen(first) == 0); | ||||
| 
 | ||||
|    return first; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,78 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (compat_snprintf.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| /* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */ | ||||
| #ifdef _MSC_VER | ||||
| 
 | ||||
| #include <retro_common.h> | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdarg.h> | ||||
| 
 | ||||
| #if _MSC_VER < 1800 | ||||
| #define va_copy(dst, src) ((dst) = (src)) | ||||
| #endif | ||||
| 
 | ||||
| #if _MSC_VER < 1300 | ||||
| #define _vscprintf c89_vscprintf_retro__ | ||||
| 
 | ||||
| static int c89_vscprintf_retro__(const char *format, va_list pargs) | ||||
| { | ||||
|    int retval; | ||||
|    va_list argcopy; | ||||
|    va_copy(argcopy, pargs); | ||||
|    retval = vsnprintf(NULL, 0, format, argcopy); | ||||
|    va_end(argcopy); | ||||
|    return retval; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */ | ||||
| 
 | ||||
| int c99_vsnprintf_retro__(char *outBuf, size_t size, const char *format, va_list ap) | ||||
| { | ||||
|    int count = -1; | ||||
| 
 | ||||
|    if (size != 0) | ||||
| #if (_MSC_VER <= 1310) | ||||
|        count = _vsnprintf(outBuf, size, format, ap); | ||||
| #else | ||||
|        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); | ||||
| #endif | ||||
|    if (count == -1) | ||||
|        count = _vscprintf(format, ap); | ||||
| 
 | ||||
|    return count; | ||||
| } | ||||
| 
 | ||||
| int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...) | ||||
| { | ||||
|    int count; | ||||
|    va_list ap; | ||||
| 
 | ||||
|    va_start(ap, format); | ||||
|    count = c99_vsnprintf_retro__(outBuf, size, format, ap); | ||||
|    va_end(ap); | ||||
| 
 | ||||
|    return count; | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,58 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (compat_strcasestr.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| #include <compat/strcasestr.h> | ||||
| 
 | ||||
| /* Pretty much strncasecmp. */ | ||||
| static int casencmp(const char *a, const char *b, size_t n) | ||||
| { | ||||
|    size_t i; | ||||
| 
 | ||||
|    for (i = 0; i < n; i++) | ||||
|    { | ||||
|       int a_lower = tolower(a[i]); | ||||
|       int b_lower = tolower(b[i]); | ||||
|       if (a_lower != b_lower) | ||||
|          return a_lower - b_lower; | ||||
|    } | ||||
| 
 | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| char *strcasestr_retro__(const char *haystack, const char *needle) | ||||
| { | ||||
|    size_t i, search_off; | ||||
|    size_t hay_len    = strlen(haystack); | ||||
|    size_t needle_len = strlen(needle); | ||||
| 
 | ||||
|    if (needle_len > hay_len) | ||||
|       return NULL; | ||||
| 
 | ||||
|    search_off = hay_len - needle_len; | ||||
|    for (i = 0; i <= search_off; i++) | ||||
|       if (!casencmp(haystack + i, needle, needle_len)) | ||||
|          return (char*)haystack + i; | ||||
| 
 | ||||
|    return NULL; | ||||
| } | ||||
|  | @ -0,0 +1,69 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (compat_strl.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| #include <compat/strl.h> | ||||
| 
 | ||||
| /* Implementation of strlcpy()/strlcat() based on OpenBSD. */ | ||||
| 
 | ||||
| #ifndef __MACH__ | ||||
| 
 | ||||
| size_t strlcpy(char *dest, const char *source, size_t size) | ||||
| { | ||||
|    size_t src_size = 0; | ||||
|    size_t        n = size; | ||||
| 
 | ||||
|    if (n) | ||||
|       while (--n && (*dest++ = *source++)) src_size++; | ||||
| 
 | ||||
|    if (!n) | ||||
|    { | ||||
|       if (size) *dest = '\0'; | ||||
|       while (*source++) src_size++; | ||||
|    } | ||||
| 
 | ||||
|    return src_size; | ||||
| } | ||||
| 
 | ||||
| size_t strlcat(char *dest, const char *source, size_t size) | ||||
| { | ||||
|    size_t len = strlen(dest); | ||||
| 
 | ||||
|    dest += len; | ||||
| 
 | ||||
|    if (len > size) | ||||
|       size = 0; | ||||
|    else | ||||
|       size -= len; | ||||
| 
 | ||||
|    return len + strlcpy(dest, source, size); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| char *strldup(const char *s, size_t n) | ||||
| { | ||||
|    char *dst = (char*)malloc(sizeof(char) * (n + 1)); | ||||
|    strlcpy(dst, s, n); | ||||
|    return dst; | ||||
| } | ||||
|  | @ -0,0 +1,44 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (compat_snprintf.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| /* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */ | ||||
| #ifdef _MSC_VER | ||||
| 
 | ||||
| #include <retro_common.h> | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdarg.h> | ||||
| 
 | ||||
| #if defined(_MSC_VER) && _MSC_VER < 1800 | ||||
| #define va_copy(dst, src) ((dst) = (src)) | ||||
| #endif | ||||
| 
 | ||||
| int c89_vscprintf_retro__(const char *format, va_list pargs) | ||||
| { | ||||
|    int retval; | ||||
|    va_list argcopy; | ||||
|    va_copy(argcopy, pargs); | ||||
|    retval = vsnprintf(NULL, 0, format, argcopy); | ||||
|    va_end(argcopy); | ||||
|    return retval; | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,37 @@ | |||
| #include <compat/fopen_utf8.h> | ||||
| #include <encodings/utf.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) | ||||
| #ifndef LEGACY_WIN32 | ||||
| #define LEGACY_WIN32 | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #undef fopen | ||||
| 
 | ||||
| FILE* fopen_utf8(const char * filename, const char * mode) | ||||
| { | ||||
| #if defined(_XBOX) | ||||
|    return fopen(filename, mode); | ||||
| #elif defined(LEGACY_WIN32) | ||||
|    FILE             *ret = NULL; | ||||
|    char * filename_local = utf8_to_local_string_alloc(filename); | ||||
| 
 | ||||
|    if (!filename_local) | ||||
|       return NULL; | ||||
|    ret = fopen(filename_local, mode); | ||||
|    if (filename_local) | ||||
|       free(filename_local); | ||||
|    return ret; | ||||
| #else | ||||
|    wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); | ||||
|    wchar_t * mode_w = utf8_to_utf16_string_alloc(mode); | ||||
|    FILE* ret = _wfopen(filename_w, mode_w); | ||||
|    free(filename_w); | ||||
|    free(mode_w); | ||||
|    return ret; | ||||
| #endif | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,10 @@ | |||
| #ifndef __LIBRETRO_SDK_CRT_STRING_H_ | ||||
| #define __LIBRETRO_SDK_CRT_STRING_H_ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| void *memcpy(void *dst, const void *src, size_t len); | ||||
| 
 | ||||
| void *memset(void *b, int c, size_t len); | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,34 @@ | |||
| #ifdef _MSC_VER | ||||
| #include <cruntime.h> | ||||
| #endif | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| void *memset(void *dst, int val, size_t count) | ||||
| { | ||||
|    void *start = dst; | ||||
| 
 | ||||
| #if defined(_M_IA64) || defined (_M_AMD64) || defined(_M_ALPHA) || defined (_M_PPC) | ||||
|    extern void RtlFillMemory(void *, size_t count, char); | ||||
| 
 | ||||
|    RtlFillMemory(dst, count, (char)val); | ||||
| #else | ||||
|    while (count--) | ||||
|    { | ||||
|       *(char*)dst = (char)val; | ||||
|       dst = (char*)dst + 1; | ||||
|    } | ||||
| #endif | ||||
| 
 | ||||
|    return start; | ||||
| } | ||||
| 
 | ||||
| void *memcpy(void *dst, const void *src, size_t len) | ||||
| { | ||||
|    size_t i; | ||||
| 
 | ||||
|    for (i = 0; i < len; i++) | ||||
|       ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; | ||||
| 
 | ||||
|    return dst; | ||||
| } | ||||
|  | @ -0,0 +1,167 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (dylib.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <dynamic/dylib.h> | ||||
| #include <encodings/utf.h> | ||||
| 
 | ||||
| #ifdef NEED_DYNAMIC | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <compat/posix_string.h> | ||||
| #include <windows.h> | ||||
| #else | ||||
| #include <dlfcn.h> | ||||
| #endif | ||||
| 
 | ||||
| /* Assume W-functions do not work below Win2K and Xbox platforms */ | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) | ||||
| 
 | ||||
| #ifndef LEGACY_WIN32 | ||||
| #define LEGACY_WIN32 | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| static char last_dyn_error[512]; | ||||
| 
 | ||||
| static void set_dl_error(void) | ||||
| { | ||||
|    DWORD err = GetLastError(); | ||||
| 
 | ||||
|    if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | | ||||
|             FORMAT_MESSAGE_FROM_SYSTEM, | ||||
|             NULL, | ||||
|             err, | ||||
|             MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), | ||||
|             last_dyn_error, | ||||
|             sizeof(last_dyn_error) - 1, | ||||
|             NULL) == 0) | ||||
|       snprintf(last_dyn_error, sizeof(last_dyn_error) - 1, | ||||
|             "unknown error %lu", err); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * dylib_load: | ||||
|  * @path                         : Path to libretro core library. | ||||
|  * | ||||
|  * Platform independent dylib loading. | ||||
|  * | ||||
|  * Returns: library handle on success, otherwise NULL. | ||||
|  **/ | ||||
| dylib_t dylib_load(const char *path) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|    int prevmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); | ||||
| #ifdef LEGACY_WIN32 | ||||
|    dylib_t lib  = LoadLibrary(path); | ||||
| #else | ||||
|    wchar_t *pathW = utf8_to_utf16_string_alloc(path); | ||||
|    dylib_t lib  = LoadLibraryW(pathW); | ||||
| 
 | ||||
|    free(pathW); | ||||
| #endif | ||||
| 
 | ||||
|    SetErrorMode(prevmode); | ||||
| 
 | ||||
|    if (!lib) | ||||
|    { | ||||
|       set_dl_error(); | ||||
|       return NULL; | ||||
|    } | ||||
|    last_dyn_error[0] = 0; | ||||
| #else | ||||
|    dylib_t lib = dlopen(path, RTLD_LAZY); | ||||
| #endif | ||||
|    return lib; | ||||
| } | ||||
| 
 | ||||
| char *dylib_error(void) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|    if (last_dyn_error[0]) | ||||
|       return last_dyn_error; | ||||
|    return NULL; | ||||
| #else | ||||
|    return (char*)dlerror(); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| function_t dylib_proc(dylib_t lib, const char *proc) | ||||
| { | ||||
|    function_t sym; | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|    sym = (function_t)GetProcAddress(lib ? | ||||
|          (HMODULE)lib : GetModuleHandle(NULL), proc); | ||||
|    if (!sym) | ||||
|    { | ||||
|       set_dl_error(); | ||||
|       return NULL; | ||||
|    } | ||||
|    last_dyn_error[0] = 0; | ||||
| #else | ||||
|    void *ptr_sym = NULL; | ||||
| 
 | ||||
|    if (lib) | ||||
|       ptr_sym = dlsym(lib, proc); | ||||
|    else | ||||
|    { | ||||
|       void *handle = dlopen(NULL, RTLD_LAZY); | ||||
|       if (handle) | ||||
|       { | ||||
|          ptr_sym = dlsym(handle, proc); | ||||
|          dlclose(handle); | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    /* Dirty hack to workaround the non-legality of
 | ||||
|     * (void*) -> fn-pointer casts. */ | ||||
|    memcpy(&sym, &ptr_sym, sizeof(void*)); | ||||
| #endif | ||||
| 
 | ||||
|    return sym; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * dylib_close: | ||||
|  * @lib                          : Library handle. | ||||
|  * | ||||
|  * Frees library handle. | ||||
|  **/ | ||||
| void dylib_close(dylib_t lib) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|    if (!FreeLibrary((HMODULE)lib)) | ||||
|       set_dl_error(); | ||||
|    last_dyn_error[0] = 0; | ||||
| #else | ||||
| #ifndef NO_DLCLOSE | ||||
|    dlclose(lib); | ||||
| #endif | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,90 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (encoding_crc32.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| #include <encodings/crc32.h> | ||||
| 
 | ||||
| static const uint32_t crc32_table[256] = { | ||||
|   0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, | ||||
|   0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, | ||||
|   0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, | ||||
|   0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, | ||||
|   0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, | ||||
|   0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, | ||||
|   0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, | ||||
|   0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, | ||||
|   0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, | ||||
|   0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, | ||||
|   0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, | ||||
|   0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, | ||||
|   0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, | ||||
|   0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, | ||||
|   0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, | ||||
|   0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, | ||||
|   0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, | ||||
|   0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, | ||||
|   0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, | ||||
|   0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, | ||||
|   0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, | ||||
|   0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, | ||||
|   0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, | ||||
|   0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, | ||||
|   0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, | ||||
|   0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, | ||||
|   0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, | ||||
|   0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, | ||||
|   0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, | ||||
|   0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, | ||||
|   0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, | ||||
|   0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, | ||||
|   0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, | ||||
|   0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, | ||||
|   0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, | ||||
|   0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, | ||||
|   0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, | ||||
|   0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, | ||||
|   0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, | ||||
|   0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, | ||||
|   0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, | ||||
|   0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, | ||||
|   0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, | ||||
|   0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, | ||||
|   0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, | ||||
|   0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, | ||||
|   0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, | ||||
|   0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, | ||||
|   0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, | ||||
|   0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, | ||||
|   0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, | ||||
|   0x2d02ef8dL | ||||
| }; | ||||
| 
 | ||||
| uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len) | ||||
| { | ||||
|    crc = crc ^ 0xffffffff; | ||||
| 
 | ||||
|    while (len--) | ||||
|       crc = crc32_table[(crc ^ (*buf++)) & 0xff] ^ (crc >> 8); | ||||
| 
 | ||||
|    return crc ^ 0xffffffff; | ||||
| } | ||||
|  | @ -0,0 +1,516 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (encoding_utf.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <stddef.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <boolean.h> | ||||
| #include <compat/strl.h> | ||||
| #include <retro_inline.h> | ||||
| 
 | ||||
| #include <encodings/utf.h> | ||||
| 
 | ||||
| #if defined(_WIN32) && !defined(_XBOX) | ||||
| #include <windows.h> | ||||
| #elif defined(_XBOX) | ||||
| #include <xtl.h> | ||||
| #endif | ||||
| 
 | ||||
| static unsigned leading_ones(uint8_t c) | ||||
| { | ||||
|    unsigned ones = 0; | ||||
|    while (c & 0x80) | ||||
|    { | ||||
|       ones++; | ||||
|       c <<= 1; | ||||
|    } | ||||
| 
 | ||||
|    return ones; | ||||
| } | ||||
| 
 | ||||
| /* Simple implementation. Assumes the sequence is
 | ||||
|  * properly synchronized and terminated. */ | ||||
| 
 | ||||
| size_t utf8_conv_utf32(uint32_t *out, size_t out_chars, | ||||
|       const char *in, size_t in_size) | ||||
| { | ||||
|    unsigned i; | ||||
|    size_t ret = 0; | ||||
|    while (in_size && out_chars) | ||||
|    { | ||||
|       unsigned extra, shift; | ||||
|       uint32_t c; | ||||
|       uint8_t first = *in++; | ||||
|       unsigned ones = leading_ones(first); | ||||
| 
 | ||||
|       if (ones > 6 || ones == 1) /* Invalid or desync. */ | ||||
|          break; | ||||
| 
 | ||||
|       extra = ones ? ones - 1 : ones; | ||||
|       if (1 + extra > in_size) /* Overflow. */ | ||||
|          break; | ||||
| 
 | ||||
|       shift = (extra - 1) * 6; | ||||
|       c     = (first & ((1 << (7 - ones)) - 1)) << (6 * extra); | ||||
| 
 | ||||
|       for (i = 0; i < extra; i++, in++, shift -= 6) | ||||
|          c |= (*in & 0x3f) << shift; | ||||
| 
 | ||||
|       *out++ = c; | ||||
|       in_size -= 1 + extra; | ||||
|       out_chars--; | ||||
|       ret++; | ||||
|    } | ||||
| 
 | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, | ||||
|      const uint16_t *in, size_t in_size) | ||||
| { | ||||
|    static uint8_t kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; | ||||
|    size_t out_pos = 0; | ||||
|    size_t in_pos  = 0; | ||||
| 
 | ||||
|    for (;;) | ||||
|    { | ||||
|       unsigned numAdds; | ||||
|       uint32_t value; | ||||
| 
 | ||||
|       if (in_pos == in_size) | ||||
|       { | ||||
|          *out_chars = out_pos; | ||||
|          return true; | ||||
|       } | ||||
|       value = in[in_pos++]; | ||||
|       if (value < 0x80) | ||||
|       { | ||||
|          if (out) | ||||
|             out[out_pos] = (char)value; | ||||
|          out_pos++; | ||||
|          continue; | ||||
|       } | ||||
| 
 | ||||
|       if (value >= 0xD800 && value < 0xE000) | ||||
|       { | ||||
|          uint32_t c2; | ||||
| 
 | ||||
|          if (value >= 0xDC00 || in_pos == in_size) | ||||
|             break; | ||||
|          c2 = in[in_pos++]; | ||||
|          if (c2 < 0xDC00 || c2 >= 0xE000) | ||||
|             break; | ||||
|          value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; | ||||
|       } | ||||
| 
 | ||||
|       for (numAdds = 1; numAdds < 5; numAdds++) | ||||
|          if (value < (((uint32_t)1) << (numAdds * 5 + 6))) | ||||
|             break; | ||||
|       if (out) | ||||
|          out[out_pos] = (char)(kUtf8Limits[numAdds - 1] | ||||
|                + (value >> (6 * numAdds))); | ||||
|       out_pos++; | ||||
|       do | ||||
|       { | ||||
|          numAdds--; | ||||
|          if (out) | ||||
|             out[out_pos] = (char)(0x80 | ||||
|                   + ((value >> (6 * numAdds)) & 0x3F)); | ||||
|          out_pos++; | ||||
|       }while (numAdds != 0); | ||||
|    } | ||||
| 
 | ||||
|    *out_chars = out_pos; | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| /* Acts mostly like strlcpy.
 | ||||
|  * | ||||
|  * Copies the given number of UTF-8 characters, | ||||
|  * but at most d_len bytes. | ||||
|  * | ||||
|  * Always NULL terminates. | ||||
|  * Does not copy half a character. | ||||
|  * | ||||
|  * Returns number of bytes. 's' is assumed valid UTF-8. | ||||
|  * Use only if 'chars' is considerably less than 'd_len'. */ | ||||
| size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars) | ||||
| { | ||||
|    const uint8_t *sb     = (const uint8_t*)s; | ||||
|    const uint8_t *sb_org = sb; | ||||
| 
 | ||||
|    if (!s) | ||||
|       return 0; | ||||
| 
 | ||||
|    while (*sb && chars-- > 0) | ||||
|    { | ||||
|       sb++; | ||||
|       while ((*sb & 0xC0) == 0x80) sb++; | ||||
|    } | ||||
| 
 | ||||
|    if ((size_t)(sb - sb_org) > d_len-1 /* NUL */) | ||||
|    { | ||||
|       sb = sb_org + d_len-1; | ||||
|       while ((*sb & 0xC0) == 0x80) sb--; | ||||
|    } | ||||
| 
 | ||||
|    memcpy(d, sb_org, sb-sb_org); | ||||
|    d[sb-sb_org] = '\0'; | ||||
| 
 | ||||
|    return sb-sb_org; | ||||
| } | ||||
| 
 | ||||
| const char *utf8skip(const char *str, size_t chars) | ||||
| { | ||||
|    const uint8_t *strb = (const uint8_t*)str; | ||||
|    if (!chars) | ||||
|       return str; | ||||
|    do | ||||
|    { | ||||
|       strb++; | ||||
|       while ((*strb & 0xC0)==0x80) strb++; | ||||
|       chars--; | ||||
|    } while(chars); | ||||
|    return (const char*)strb; | ||||
| } | ||||
| 
 | ||||
| size_t utf8len(const char *string) | ||||
| { | ||||
|    size_t ret = 0; | ||||
| 
 | ||||
|    if (!string) | ||||
|       return 0; | ||||
| 
 | ||||
|    while (*string) | ||||
|    { | ||||
|       if ((*string & 0xC0) != 0x80) | ||||
|          ret++; | ||||
|       string++; | ||||
|    } | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| static uint8_t utf8_walkbyte(const char **string) | ||||
| { | ||||
|    return *((*string)++); | ||||
| } | ||||
| 
 | ||||
| /* Does not validate the input, returns garbage if it's not UTF-8. */ | ||||
| uint32_t utf8_walk(const char **string) | ||||
| { | ||||
|    uint8_t first = utf8_walkbyte(string); | ||||
|    uint32_t ret  = 0; | ||||
| 
 | ||||
|    if (first < 128) | ||||
|       return first; | ||||
| 
 | ||||
|    ret    = (ret << 6) | (utf8_walkbyte(string) & 0x3F); | ||||
|    if (first >= 0xE0) | ||||
|       ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F); | ||||
|    if (first >= 0xF0) | ||||
|       ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F); | ||||
| 
 | ||||
|    if (first >= 0xF0) | ||||
|       return ret | (first & 7) << 18; | ||||
|    if (first >= 0xE0) | ||||
|       return ret | (first & 15) << 12; | ||||
|    return ret | (first & 31) << 6; | ||||
| } | ||||
| 
 | ||||
| static bool utf16_to_char(uint8_t **utf_data, | ||||
|       size_t *dest_len, const uint16_t *in) | ||||
| { | ||||
|    unsigned len    = 0; | ||||
| 
 | ||||
|    while (in[len] != '\0') | ||||
|       len++; | ||||
| 
 | ||||
|    utf16_conv_utf8(NULL, dest_len, in, len); | ||||
|    *dest_len  += 1; | ||||
|    *utf_data   = (uint8_t*)malloc(*dest_len); | ||||
|    if (*utf_data == 0) | ||||
|       return false; | ||||
| 
 | ||||
|    return utf16_conv_utf8(*utf_data, dest_len, in, len); | ||||
| } | ||||
| 
 | ||||
| bool utf16_to_char_string(const uint16_t *in, char *s, size_t len) | ||||
| { | ||||
|    size_t     dest_len  = 0; | ||||
|    uint8_t *utf16_data  = NULL; | ||||
|    bool            ret  = utf16_to_char(&utf16_data, &dest_len, in); | ||||
| 
 | ||||
|    if (ret) | ||||
|    { | ||||
|       utf16_data[dest_len] = 0; | ||||
|       strlcpy(s, (const char*)utf16_data, len); | ||||
|    } | ||||
| 
 | ||||
|    free(utf16_data); | ||||
|    utf16_data = NULL; | ||||
| 
 | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| /* Returned pointer MUST be freed by the caller if non-NULL. */ | ||||
| static char* mb_to_mb_string_alloc(const char *str, | ||||
|       enum CodePage cp_in, enum CodePage cp_out) | ||||
| { | ||||
|    char *path_buf         = NULL; | ||||
|    wchar_t *path_buf_wide = NULL; | ||||
|    int path_buf_len       = 0; | ||||
|    int path_buf_wide_len  = 0; | ||||
| 
 | ||||
|    if (!str || !*str) | ||||
|       return NULL; | ||||
| 
 | ||||
|    (void)path_buf; | ||||
|    (void)path_buf_wide; | ||||
|    (void)path_buf_len; | ||||
|    (void)path_buf_wide_len; | ||||
| 
 | ||||
| #if !defined(_WIN32) || defined(_XBOX) | ||||
|    /* assume string needs no modification if not on Windows */ | ||||
|    return strdup(str); | ||||
| #else | ||||
| #ifdef UNICODE | ||||
|    /* TODO/FIXME: Not implemented. */ | ||||
|    return strdup(str); | ||||
| #else | ||||
| 
 | ||||
|    /* Windows 95 will return 0 from these functions with a UTF8 codepage set without MSLU. From an unknown MSDN version (others omit this info):
 | ||||
|     *   - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-8. When this is set, dwFlags must be zero. | ||||
|     *   - Windows 95: Under the Microsoft Layer for Unicode, MultiByteToWideChar also supports CP_UTF7 and CP_UTF8. | ||||
|     */ | ||||
|    path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0); | ||||
| 
 | ||||
|    if (path_buf_wide_len) | ||||
|    { | ||||
|       path_buf_wide = (wchar_t*) | ||||
|          calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t)); | ||||
| 
 | ||||
|       if (path_buf_wide) | ||||
|       { | ||||
|          MultiByteToWideChar(cp_in, 0, | ||||
|                str, -1, path_buf_wide, path_buf_wide_len); | ||||
| 
 | ||||
|          if (*path_buf_wide) | ||||
|          { | ||||
|             path_buf_len = WideCharToMultiByte(cp_out, 0, | ||||
|                   path_buf_wide, -1, NULL, 0, NULL, NULL); | ||||
| 
 | ||||
|             if (path_buf_len) | ||||
|             { | ||||
|                path_buf = (char*) | ||||
|                   calloc(path_buf_len + sizeof(char), sizeof(char)); | ||||
| 
 | ||||
|                if (path_buf) | ||||
|                { | ||||
|                   WideCharToMultiByte(cp_out, 0, | ||||
|                         path_buf_wide, -1, path_buf, | ||||
|                         path_buf_len, NULL, NULL); | ||||
| 
 | ||||
|                   free(path_buf_wide); | ||||
| 
 | ||||
|                   if (*path_buf) | ||||
|                      return path_buf; | ||||
| 
 | ||||
|                   free(path_buf); | ||||
|                   return NULL; | ||||
|                } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                free(path_buf_wide); | ||||
|                return strdup(str); | ||||
|             } | ||||
|          } | ||||
|       } | ||||
|    } | ||||
|    else | ||||
|       return strdup(str); | ||||
| 
 | ||||
|    if (path_buf_wide) | ||||
|       free(path_buf_wide); | ||||
| 
 | ||||
|    return NULL; | ||||
| #endif | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* Returned pointer MUST be freed by the caller if non-NULL. */ | ||||
| char* utf8_to_local_string_alloc(const char *str) | ||||
| { | ||||
|    return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL); | ||||
| } | ||||
| 
 | ||||
| /* Returned pointer MUST be freed by the caller if non-NULL. */ | ||||
| char* local_to_utf8_string_alloc(const char *str) | ||||
| { | ||||
|    return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8); | ||||
| } | ||||
| 
 | ||||
| /* Returned pointer MUST be freed by the caller if non-NULL. */ | ||||
| wchar_t* utf8_to_utf16_string_alloc(const char *str) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|    int len = 0; | ||||
|    int out_len = 0; | ||||
| #else | ||||
|    size_t len = 0; | ||||
|    size_t out_len = 0; | ||||
| #endif | ||||
|    wchar_t *buf = NULL; | ||||
| 
 | ||||
|    if (!str || !*str) | ||||
|       return NULL; | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|    len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); | ||||
| 
 | ||||
|    if (len) | ||||
|    { | ||||
|       buf = (wchar_t*)calloc(len, sizeof(wchar_t)); | ||||
| 
 | ||||
|       if (!buf) | ||||
|          return NULL; | ||||
| 
 | ||||
|       out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       /* fallback to ANSI codepage instead */ | ||||
|       len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); | ||||
| 
 | ||||
|       if (len) | ||||
|       { | ||||
|          buf = (wchar_t*)calloc(len, sizeof(wchar_t)); | ||||
| 
 | ||||
|          if (!buf) | ||||
|             return NULL; | ||||
| 
 | ||||
|          out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len); | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    if (out_len < 0) | ||||
|    { | ||||
|       free(buf); | ||||
|       return NULL; | ||||
|    } | ||||
| #else | ||||
|    /* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */ | ||||
|    len = mbstowcs(NULL, str, 0) + 1; | ||||
| 
 | ||||
|    if (len) | ||||
|    { | ||||
|       buf = (wchar_t*)calloc(len, sizeof(wchar_t)); | ||||
| 
 | ||||
|       if (!buf) | ||||
|          return NULL; | ||||
| 
 | ||||
|       out_len = mbstowcs(buf, str, len); | ||||
|    } | ||||
| 
 | ||||
|    if (out_len == (size_t)-1) | ||||
|    { | ||||
|       free(buf); | ||||
|       return NULL; | ||||
|    } | ||||
| #endif | ||||
| 
 | ||||
|    return buf; | ||||
| } | ||||
| 
 | ||||
| /* Returned pointer MUST be freed by the caller if non-NULL. */ | ||||
| char* utf16_to_utf8_string_alloc(const wchar_t *str) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|    int len = 0; | ||||
|    int out_len = 0; | ||||
| #else | ||||
|    size_t len = 0; | ||||
|    size_t out_len = 0; | ||||
| #endif | ||||
|    char *buf = NULL; | ||||
| 
 | ||||
|    if (!str || !*str) | ||||
|       return NULL; | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|    len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); | ||||
| 
 | ||||
|    if (len) | ||||
|    { | ||||
|       buf = (char*)calloc(len, sizeof(char)); | ||||
| 
 | ||||
|       if (!buf) | ||||
|          return NULL; | ||||
| 
 | ||||
|       out_len = WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, len, NULL, NULL); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       /* fallback to ANSI codepage instead */ | ||||
|       len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); | ||||
| 
 | ||||
|       if (len) | ||||
|       { | ||||
|          buf = (char*)calloc(len, sizeof(char)); | ||||
| 
 | ||||
|          if (!buf) | ||||
|             return NULL; | ||||
| 
 | ||||
|          out_len = WideCharToMultiByte(CP_ACP, 0, str, -1, buf, len, NULL, NULL); | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    if (out_len < 0) | ||||
|    { | ||||
|       free(buf); | ||||
|       return NULL; | ||||
|    } | ||||
| #else | ||||
|    /* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */ | ||||
|    len = wcstombs(NULL, str, 0) + 1; | ||||
| 
 | ||||
|    if (len) | ||||
|    { | ||||
|       buf = (char*)calloc(len, sizeof(char)); | ||||
| 
 | ||||
|       if (!buf) | ||||
|          return NULL; | ||||
| 
 | ||||
|       out_len = wcstombs(buf, str, len); | ||||
|    } | ||||
| 
 | ||||
|    if (out_len == (size_t)-1) | ||||
|    { | ||||
|       free(buf); | ||||
|       return NULL; | ||||
|    } | ||||
| #endif | ||||
| 
 | ||||
|    return buf; | ||||
| } | ||||
|  | @ -0,0 +1,809 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (features_cpu.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #if defined(_WIN32) | ||||
| #include <direct.h> | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <compat/strl.h> | ||||
| #include <streams/file_stream.h> | ||||
| #include <libretro.h> | ||||
| #include <features/features_cpu.h> | ||||
| #include <retro_timers.h> | ||||
| 
 | ||||
| #if defined(_WIN32) && !defined(_XBOX) | ||||
| #include <windows.h> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__CELLOS_LV2__) | ||||
| #ifndef _PPU_INTRINSICS_H | ||||
| #include <ppu_intrinsics.h> | ||||
| #endif | ||||
| #elif defined(_XBOX360) | ||||
| #include <PPCIntrinsics.h> | ||||
| #elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID) || defined(__QNX__) || defined(DJGPP) | ||||
| /* POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present. */ | ||||
| #include <time.h> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__QNX__) && !defined(CLOCK_MONOTONIC) | ||||
| #define CLOCK_MONOTONIC 2 | ||||
| #endif | ||||
| 
 | ||||
| #if defined(PSP) | ||||
| #include <pspkernel.h> | ||||
| #include <sys/time.h> | ||||
| #include <psprtc.h> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(VITA) | ||||
| #include <psp2/kernel/processmgr.h> | ||||
| #include <psp2/rtc.h> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__PSL1GHT__) | ||||
| #include <sys/time.h> | ||||
| #elif defined(__CELLOS_LV2__) | ||||
| #include <sys/sys_time.h> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef GEKKO | ||||
| #include <ogc/lwp_watchdog.h> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef WIIU | ||||
| #include <wiiu/os/time.h> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef SWITCH | ||||
| #include <libtransistor/types.h> | ||||
| #include <libtransistor/svc.h> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(_3DS) | ||||
| #include <3ds/svc.h> | ||||
| #include <3ds/os.h> | ||||
| #include <3ds/services/cfgu.h> | ||||
| #endif | ||||
| 
 | ||||
| /* iOS/OSX specific. Lacks clock_gettime(), so implement it. */ | ||||
| #ifdef __MACH__ | ||||
| #include <sys/time.h> | ||||
| 
 | ||||
| #ifndef CLOCK_MONOTONIC | ||||
| #define CLOCK_MONOTONIC 0 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CLOCK_REALTIME | ||||
| #define CLOCK_REALTIME 0 | ||||
| #endif | ||||
| 
 | ||||
| /* this function is part of iOS 10 now */ | ||||
| static int ra_clock_gettime(int clk_ik, struct timespec *t) | ||||
| { | ||||
|    struct timeval now; | ||||
|    int rv = gettimeofday(&now, NULL); | ||||
|    if (rv) | ||||
|       return rv; | ||||
|    t->tv_sec  = now.tv_sec; | ||||
|    t->tv_nsec = now.tv_usec * 1000; | ||||
|    return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__MACH__) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000 | ||||
| #else | ||||
| #define ra_clock_gettime clock_gettime | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #ifdef EMSCRIPTEN | ||||
| #include <emscripten.h> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(BSD) || defined(__APPLE__) | ||||
| #include <sys/sysctl.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <string.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * cpu_features_get_perf_counter: | ||||
|  * | ||||
|  * Gets performance counter. | ||||
|  * | ||||
|  * Returns: performance counter. | ||||
|  **/ | ||||
| retro_perf_tick_t cpu_features_get_perf_counter(void) | ||||
| { | ||||
|    retro_perf_tick_t time_ticks = 0; | ||||
| #if defined(_WIN32) | ||||
|    long tv_sec, tv_usec; | ||||
| #if defined(_MSC_VER) && _MSC_VER <= 1200 | ||||
|    static const unsigned __int64 epoch = 11644473600000000; | ||||
| #else | ||||
|    static const unsigned __int64 epoch = 11644473600000000ULL; | ||||
| #endif | ||||
|    FILETIME file_time; | ||||
|    SYSTEMTIME system_time; | ||||
|    ULARGE_INTEGER ularge; | ||||
| 
 | ||||
|    GetSystemTime(&system_time); | ||||
|    SystemTimeToFileTime(&system_time, &file_time); | ||||
|    ularge.LowPart  = file_time.dwLowDateTime; | ||||
|    ularge.HighPart = file_time.dwHighDateTime; | ||||
| 
 | ||||
|    tv_sec     = (long)((ularge.QuadPart - epoch) / 10000000L); | ||||
|    tv_usec    = (long)(system_time.wMilliseconds * 1000); | ||||
|    time_ticks = (1000000 * tv_sec + tv_usec); | ||||
| #elif defined(__linux__) || defined(__QNX__) || defined(__MACH__) | ||||
|    struct timespec tv = {0}; | ||||
|    if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) == 0) | ||||
|       time_ticks = (retro_perf_tick_t)tv.tv_sec * 1000000000 + | ||||
|          (retro_perf_tick_t)tv.tv_nsec; | ||||
| 
 | ||||
| #elif defined(__GNUC__) && defined(__i386__) || defined(__i486__) || defined(__i686__) | ||||
|    __asm__ volatile ("rdtsc" : "=A" (time_ticks)); | ||||
| #elif defined(__GNUC__) && defined(__x86_64__) | ||||
|    unsigned a, d; | ||||
|    __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); | ||||
|    time_ticks = (retro_perf_tick_t)a | ((retro_perf_tick_t)d << 32); | ||||
| #elif defined(__ARM_ARCH_6__) | ||||
|    __asm__ volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"(time_ticks) ); | ||||
| #elif defined(__CELLOS_LV2__) || defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__) | ||||
|    time_ticks = __mftb(); | ||||
| #elif defined(GEKKO) | ||||
|    time_ticks = gettime(); | ||||
| #elif defined(PSP) | ||||
|    sceRtcGetCurrentTick((uint64_t*)&time_ticks); | ||||
| #elif defined(VITA) | ||||
|    sceRtcGetCurrentTick((SceRtcTick*)&time_ticks); | ||||
| #elif defined(_3DS) | ||||
|    time_ticks = svcGetSystemTick(); | ||||
| #elif defined(WIIU) | ||||
|    time_ticks = OSGetSystemTime(); | ||||
| #elif defined(__mips__) | ||||
|    struct timeval tv; | ||||
|    gettimeofday(&tv,NULL); | ||||
|    time_ticks = (1000000 * tv.tv_sec + tv.tv_usec); | ||||
| #endif | ||||
| 
 | ||||
|    return time_ticks; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * cpu_features_get_time_usec: | ||||
|  * | ||||
|  * Gets time in microseconds. | ||||
|  * | ||||
|  * Returns: time in microseconds. | ||||
|  **/ | ||||
| retro_time_t cpu_features_get_time_usec(void) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|    static LARGE_INTEGER freq; | ||||
|    LARGE_INTEGER count; | ||||
| 
 | ||||
|    /* Frequency is guaranteed to not change. */ | ||||
|    if (!freq.QuadPart && !QueryPerformanceFrequency(&freq)) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (!QueryPerformanceCounter(&count)) | ||||
|       return 0; | ||||
|    return count.QuadPart * 1000000 / freq.QuadPart; | ||||
| #elif defined(__CELLOS_LV2__) | ||||
|    return sys_time_get_system_time(); | ||||
| #elif defined(GEKKO) | ||||
|    return ticks_to_microsecs(gettime()); | ||||
| #elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__) | ||||
|    struct timespec tv = {0}; | ||||
|    if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) < 0) | ||||
|       return 0; | ||||
|    return tv.tv_sec * INT64_C(1000000) + (tv.tv_nsec + 500) / 1000; | ||||
| #elif defined(EMSCRIPTEN) | ||||
|    return emscripten_get_now() * 1000; | ||||
| #elif defined(__mips__) || defined(DJGPP) | ||||
|    struct timeval tv; | ||||
|    gettimeofday(&tv,NULL); | ||||
|    return (1000000 * tv.tv_sec + tv.tv_usec); | ||||
| #elif defined(_3DS) | ||||
|    return osGetTime() * 1000; | ||||
| #elif defined(VITA) | ||||
|    return sceKernelGetProcessTimeWide(); | ||||
| #elif defined(WIIU) | ||||
|    return ticks_to_us(OSGetSystemTime()); | ||||
| #elif defined(SWITCH) | ||||
|    return (svcGetSystemTick() * 10) / 192; | ||||
| #else | ||||
| #error "Your platform does not have a timer function implemented in cpu_features_get_time_usec(). Cannot continue." | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| #if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i686__) || (defined(_M_X64) && _MSC_VER > 1310) || (defined(_M_IX86)  && _MSC_VER > 1310) | ||||
| #define CPU_X86 | ||||
| #endif | ||||
| 
 | ||||
| #if defined(_MSC_VER) && !defined(_XBOX) | ||||
| #if (_MSC_VER > 1310) | ||||
| #include <intrin.h> | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| #if defined(CPU_X86) && !defined(__MACH__) | ||||
| void x86_cpuid(int func, int flags[4]) | ||||
| { | ||||
|    /* On Android, we compile RetroArch with PIC, and we
 | ||||
|     * are not allowed to clobber the ebx register. */ | ||||
| #ifdef __x86_64__ | ||||
| #define REG_b "rbx" | ||||
| #define REG_S "rsi" | ||||
| #else | ||||
| #define REG_b "ebx" | ||||
| #define REG_S "esi" | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__GNUC__) | ||||
|    __asm__ volatile ( | ||||
|          "mov %%" REG_b ", %%" REG_S "\n" | ||||
|          "cpuid\n" | ||||
|          "xchg %%" REG_b ", %%" REG_S "\n" | ||||
|          : "=a"(flags[0]), "=S"(flags[1]), "=c"(flags[2]), "=d"(flags[3]) | ||||
|          : "a"(func)); | ||||
| #elif defined(_MSC_VER) | ||||
|    __cpuid(flags, func); | ||||
| #else | ||||
|    printf("Unknown compiler. Cannot check CPUID with inline assembly.\n"); | ||||
|    memset(flags, 0, 4 * sizeof(int)); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* Only runs on i686 and above. Needs to be conditionally run. */ | ||||
| static uint64_t xgetbv_x86(uint32_t idx) | ||||
| { | ||||
| #if defined(__GNUC__) | ||||
|    uint32_t eax, edx; | ||||
|    __asm__ volatile ( | ||||
|          /* Older GCC versions (Apple's GCC for example) do
 | ||||
|           * not understand xgetbv instruction. | ||||
|           * Stamp out the machine code directly. | ||||
|           */ | ||||
|          ".byte 0x0f, 0x01, 0xd0\n" | ||||
|          : "=a"(eax), "=d"(edx) : "c"(idx)); | ||||
|    return ((uint64_t)edx << 32) | eax; | ||||
| #elif _MSC_FULL_VER >= 160040219 | ||||
|    /* Intrinsic only works on 2010 SP1 and above. */ | ||||
|    return _xgetbv(idx); | ||||
| #else | ||||
|    printf("Unknown compiler. Cannot check xgetbv bits.\n"); | ||||
|    return 0; | ||||
| #endif | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__ARM_NEON__) | ||||
| static void arm_enable_runfast_mode(void) | ||||
| { | ||||
|    /* RunFast mode. Enables flush-to-zero and some
 | ||||
|     * floating point optimizations. */ | ||||
|    static const unsigned x = 0x04086060; | ||||
|    static const unsigned y = 0x03000000; | ||||
|    int r; | ||||
|    __asm__ volatile( | ||||
|          "fmrx	%0, fpscr   \n\t" /* r0 = FPSCR */ | ||||
|          "and	%0, %0, %1  \n\t" /* r0 = r0 & 0x04086060 */ | ||||
|          "orr	%0, %0, %2  \n\t" /* r0 = r0 | 0x03000000 */ | ||||
|          "fmxr	fpscr, %0   \n\t" /* FPSCR = r0 */ | ||||
|          : "=r"(r) | ||||
|          : "r"(x), "r"(y) | ||||
|         ); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__linux__) && !defined(CPU_X86) | ||||
| static unsigned char check_arm_cpu_feature(const char* feature) | ||||
| { | ||||
|    char line[1024]; | ||||
|    unsigned char status = 0; | ||||
|    RFILE *fp = filestream_open("/proc/cpuinfo", | ||||
|          RETRO_VFS_FILE_ACCESS_READ, | ||||
|          RETRO_VFS_FILE_ACCESS_HINT_NONE); | ||||
| 
 | ||||
|    if (!fp) | ||||
|       return 0; | ||||
| 
 | ||||
|    while (filestream_gets(fp, line, sizeof(line)) != NULL) | ||||
|    { | ||||
|       if (strncmp(line, "Features\t: ", 11)) | ||||
|          continue; | ||||
| 
 | ||||
|       if (strstr(line + 11, feature) != NULL) | ||||
|          status = 1; | ||||
| 
 | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    filestream_close(fp); | ||||
| 
 | ||||
|    return status; | ||||
| } | ||||
| 
 | ||||
| #if !defined(_SC_NPROCESSORS_ONLN) | ||||
| /* Parse an decimal integer starting from 'input', but not going further
 | ||||
|  * than 'limit'. Return the value into '*result'. | ||||
|  * | ||||
|  * NOTE: Does not skip over leading spaces, or deal with sign characters. | ||||
|  * NOTE: Ignores overflows. | ||||
|  * | ||||
|  * The function returns NULL in case of error (bad format), or the new | ||||
|  * position after the decimal number in case of success (which will always | ||||
|  * be <= 'limit'). | ||||
|  */ | ||||
| static const char *parse_decimal(const char* input, | ||||
|       const char* limit, int* result) | ||||
| { | ||||
|     const char* p = input; | ||||
|     int       val = 0; | ||||
| 
 | ||||
|     while (p < limit) | ||||
|     { | ||||
|         int d = (*p - '0'); | ||||
|         if ((unsigned)d >= 10U) | ||||
|             break; | ||||
|         val = val*10 + d; | ||||
|         p++; | ||||
|     } | ||||
|     if (p == input) | ||||
|         return NULL; | ||||
| 
 | ||||
|     *result = val; | ||||
|     return p; | ||||
| } | ||||
| 
 | ||||
| /* Parse a textual list of cpus and store the result inside a CpuList object.
 | ||||
|  * Input format is the following: | ||||
|  * - comma-separated list of items (no spaces) | ||||
|  * - each item is either a single decimal number (cpu index), or a range made | ||||
|  *   of two numbers separated by a single dash (-). Ranges are inclusive. | ||||
|  * | ||||
|  * Examples:   0 | ||||
|  *             2,4-127,128-143 | ||||
|  *             0-1 | ||||
|  */ | ||||
| static void cpulist_parse(CpuList* list, char **buf, ssize_t length) | ||||
| { | ||||
|    const char* p   = (const char*)buf; | ||||
|    const char* end = p + length; | ||||
| 
 | ||||
|    /* NOTE: the input line coming from sysfs typically contains a
 | ||||
|     * trailing newline, so take care of it in the code below | ||||
|     */ | ||||
|    while (p < end && *p != '\n') | ||||
|    { | ||||
|       int val, start_value, end_value; | ||||
|       /* Find the end of current item, and put it into 'q' */ | ||||
|       const char *q = (const char*)memchr(p, ',', end-p); | ||||
| 
 | ||||
|       if (!q) | ||||
|          q = end; | ||||
| 
 | ||||
|       /* Get first value */ | ||||
|       p = parse_decimal(p, q, &start_value); | ||||
|       if (p == NULL) | ||||
|          return; | ||||
| 
 | ||||
|       end_value = start_value; | ||||
| 
 | ||||
|       /* If we're not at the end of the item, expect a dash and
 | ||||
|        * and integer; extract end value. | ||||
|        */ | ||||
|       if (p < q && *p == '-') | ||||
|       { | ||||
|          p = parse_decimal(p+1, q, &end_value); | ||||
|          if (p == NULL) | ||||
|             return; | ||||
|       } | ||||
| 
 | ||||
|       /* Set bits CPU list bits */ | ||||
|       for (val = start_value; val <= end_value; val++) | ||||
|       { | ||||
|          if ((unsigned)val < 32) | ||||
|             list->mask |= (uint32_t)(1U << val); | ||||
|       } | ||||
| 
 | ||||
|       /* Jump to next item */ | ||||
|       p = q; | ||||
|       if (p < end) | ||||
|          p++; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| /* Read a CPU list from one sysfs file */ | ||||
| static void cpulist_read_from(CpuList* list, const char* filename) | ||||
| { | ||||
|    ssize_t length; | ||||
|    char *buf  = NULL; | ||||
| 
 | ||||
|    list->mask = 0; | ||||
| 
 | ||||
|    if (filestream_read_file(filename, (void**)&buf, &length) != 1) | ||||
|       return; | ||||
| 
 | ||||
|    cpulist_parse(list, &buf, length); | ||||
|    if (buf) | ||||
|       free(buf); | ||||
|    buf = NULL; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * cpu_features_get_core_amount: | ||||
|  * | ||||
|  * Gets the amount of available CPU cores. | ||||
|  * | ||||
|  * Returns: amount of CPU cores available. | ||||
|  **/ | ||||
| unsigned cpu_features_get_core_amount(void) | ||||
| { | ||||
| #if defined(_WIN32) && !defined(_XBOX) | ||||
|    /* Win32 */ | ||||
|    SYSTEM_INFO sysinfo; | ||||
|    GetSystemInfo(&sysinfo); | ||||
|    return sysinfo.dwNumberOfProcessors; | ||||
| #elif defined(GEKKO) | ||||
|    return 1; | ||||
| #elif defined(PSP) | ||||
|    return 1; | ||||
| #elif defined(VITA) | ||||
|    return 4; | ||||
| #elif defined(_3DS) | ||||
|    u8 device_model = 0xFF; | ||||
|    CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ | ||||
|    switch (device_model) | ||||
|    { | ||||
| 		case 0: | ||||
| 		case 1: | ||||
| 		case 3: | ||||
| 			/*Old 3/2DS*/ | ||||
| 			return 2; | ||||
| 	    | ||||
| 		case 2: | ||||
| 		case 4: | ||||
| 		case 5: | ||||
| 			/*New 3/2DS*/ | ||||
| 			return 4; | ||||
| 	    | ||||
| 		default: | ||||
| 			/*Unknown Device Or Check Failed*/ | ||||
| 			break; | ||||
|    } | ||||
|    return 1; | ||||
| #elif defined(WIIU) | ||||
|    return 3; | ||||
| #elif defined(_SC_NPROCESSORS_ONLN) | ||||
|    /* Linux, most UNIX-likes. */ | ||||
|    long ret = sysconf(_SC_NPROCESSORS_ONLN); | ||||
|    if (ret <= 0) | ||||
|       return (unsigned)1; | ||||
|    return (unsigned)ret; | ||||
| #elif defined(BSD) || defined(__APPLE__) | ||||
|    /* BSD */ | ||||
|    /* Copypasta from stackoverflow, dunno if it works. */ | ||||
|    int num_cpu = 0; | ||||
|    int mib[4]; | ||||
|    size_t len = sizeof(num_cpu); | ||||
| 
 | ||||
|    mib[0] = CTL_HW; | ||||
|    mib[1] = HW_AVAILCPU; | ||||
|    sysctl(mib, 2, &num_cpu, &len, NULL, 0); | ||||
|    if (num_cpu < 1) | ||||
|    { | ||||
|       mib[1] = HW_NCPU; | ||||
|       sysctl(mib, 2, &num_cpu, &len, NULL, 0); | ||||
|       if (num_cpu < 1) | ||||
|          num_cpu = 1; | ||||
|    } | ||||
|    return num_cpu; | ||||
| #elif defined(__linux__) | ||||
|    CpuList  cpus_present[1]; | ||||
|    CpuList  cpus_possible[1]; | ||||
|    int amount = 0; | ||||
| 
 | ||||
|    cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present"); | ||||
|    cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible"); | ||||
| 
 | ||||
|    /* Compute the intersection of both sets to get the actual number of
 | ||||
|     * CPU cores that can be used on this device by the kernel. | ||||
|     */ | ||||
|    cpus_present->mask &= cpus_possible->mask; | ||||
|    amount              = __builtin_popcount(cpus_present->mask); | ||||
| 
 | ||||
|    if (amount == 0) | ||||
|       return 1; | ||||
|    return amount; | ||||
| #elif defined(_XBOX360) | ||||
|    return 3; | ||||
| #else | ||||
|    /* No idea, assume single core. */ | ||||
|    return 1; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* According to http://en.wikipedia.org/wiki/CPUID */ | ||||
| #define VENDOR_INTEL_b  0x756e6547 | ||||
| #define VENDOR_INTEL_c  0x6c65746e | ||||
| #define VENDOR_INTEL_d  0x49656e69 | ||||
| 
 | ||||
| /**
 | ||||
|  * cpu_features_get: | ||||
|  * | ||||
|  * Gets CPU features.. | ||||
|  * | ||||
|  * Returns: bitmask of all CPU features available. | ||||
|  **/ | ||||
| uint64_t cpu_features_get(void) | ||||
| { | ||||
|    int flags[4]; | ||||
|    int vendor_shuffle[3]; | ||||
|    char vendor[13]; | ||||
|    size_t len          = 0; | ||||
|    uint64_t cpu_flags  = 0; | ||||
|    uint64_t cpu        = 0; | ||||
|    unsigned max_flag   = 0; | ||||
| #if defined(CPU_X86) && !defined(__MACH__) | ||||
|    int vendor_is_intel = 0; | ||||
|    const int avx_flags = (1 << 27) | (1 << 28); | ||||
| #endif | ||||
| 
 | ||||
|    char buf[sizeof(" MMX MMXEXT SSE SSE2 SSE3 SSSE3 SS4 SSE4.2 AES AVX AVX2 NEON VMX VMX128 VFPU PS")]; | ||||
| 
 | ||||
|    memset(buf, 0, sizeof(buf)); | ||||
| 
 | ||||
|    (void)len; | ||||
|    (void)cpu_flags; | ||||
|    (void)flags; | ||||
|    (void)max_flag; | ||||
|    (void)vendor; | ||||
|    (void)vendor_shuffle; | ||||
| 
 | ||||
| #if defined(__MACH__) | ||||
|    len     = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.mmx", NULL, &len, NULL, 0) == 0) | ||||
|    { | ||||
|       cpu |= RETRO_SIMD_MMX; | ||||
|       cpu |= RETRO_SIMD_MMXEXT; | ||||
|    } | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.floatingpoint", NULL, &len, NULL, 0) == 0) | ||||
|    { | ||||
|       cpu |= RETRO_SIMD_CMOV; | ||||
|    } | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.sse", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_SSE; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.sse2", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_SSE2; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.sse3", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_SSE3; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.supplementalsse3", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_SSSE3; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.sse4_1", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_SSE4; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.sse4_2", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_SSE42; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.aes", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_AES; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.avx1_0", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_AVX; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.avx2_0", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_AVX2; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.altivec", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_VMX; | ||||
| 
 | ||||
|    len            = sizeof(size_t); | ||||
|    if (sysctlbyname("hw.optional.neon", NULL, &len, NULL, 0) == 0) | ||||
|       cpu |= RETRO_SIMD_NEON; | ||||
| 
 | ||||
| #elif defined(CPU_X86) | ||||
|    (void)avx_flags; | ||||
| 
 | ||||
|    x86_cpuid(0, flags); | ||||
|    vendor_shuffle[0] = flags[1]; | ||||
|    vendor_shuffle[1] = flags[3]; | ||||
|    vendor_shuffle[2] = flags[2]; | ||||
| 
 | ||||
|    vendor[0]         = '\0'; | ||||
|    memcpy(vendor, vendor_shuffle, sizeof(vendor_shuffle)); | ||||
| 
 | ||||
|    /* printf("[CPUID]: Vendor: %s\n", vendor); */ | ||||
| 
 | ||||
|    vendor_is_intel = ( | ||||
|          flags[1] == VENDOR_INTEL_b && | ||||
|          flags[2] == VENDOR_INTEL_c && | ||||
|          flags[3] == VENDOR_INTEL_d); | ||||
| 
 | ||||
|    max_flag = flags[0]; | ||||
|    if (max_flag < 1) /* Does CPUID not support func = 1? (unlikely ...) */ | ||||
|       return 0; | ||||
| 
 | ||||
|    x86_cpuid(1, flags); | ||||
| 
 | ||||
|    if (flags[3] & (1 << 15)) | ||||
|       cpu |= RETRO_SIMD_CMOV; | ||||
| 
 | ||||
|    if (flags[3] & (1 << 23)) | ||||
|       cpu |= RETRO_SIMD_MMX; | ||||
| 
 | ||||
|    if (flags[3] & (1 << 25)) | ||||
|    { | ||||
|       /* SSE also implies MMXEXT (according to FFmpeg source). */ | ||||
|       cpu |= RETRO_SIMD_SSE; | ||||
|       cpu |= RETRO_SIMD_MMXEXT; | ||||
|    } | ||||
| 
 | ||||
| 
 | ||||
|    if (flags[3] & (1 << 26)) | ||||
|       cpu |= RETRO_SIMD_SSE2; | ||||
| 
 | ||||
|    if (flags[2] & (1 << 0)) | ||||
|       cpu |= RETRO_SIMD_SSE3; | ||||
| 
 | ||||
|    if (flags[2] & (1 << 9)) | ||||
|       cpu |= RETRO_SIMD_SSSE3; | ||||
| 
 | ||||
|    if (flags[2] & (1 << 19)) | ||||
|       cpu |= RETRO_SIMD_SSE4; | ||||
| 
 | ||||
|    if (flags[2] & (1 << 20)) | ||||
|       cpu |= RETRO_SIMD_SSE42; | ||||
| 
 | ||||
|    if ((flags[2] & (1 << 23))) | ||||
|       cpu |= RETRO_SIMD_POPCNT; | ||||
| 
 | ||||
|    if (vendor_is_intel && (flags[2] & (1 << 22))) | ||||
|       cpu |= RETRO_SIMD_MOVBE; | ||||
| 
 | ||||
|    if (flags[2] & (1 << 25)) | ||||
|       cpu |= RETRO_SIMD_AES; | ||||
| 
 | ||||
| 
 | ||||
|    /* Must only perform xgetbv check if we have
 | ||||
|     * AVX CPU support (guaranteed to have at least i686). */ | ||||
|    if (((flags[2] & avx_flags) == avx_flags) | ||||
|          && ((xgetbv_x86(0) & 0x6) == 0x6)) | ||||
|       cpu |= RETRO_SIMD_AVX; | ||||
| 
 | ||||
|    if (max_flag >= 7) | ||||
|    { | ||||
|       x86_cpuid(7, flags); | ||||
|       if (flags[1] & (1 << 5)) | ||||
|          cpu |= RETRO_SIMD_AVX2; | ||||
|    } | ||||
| 
 | ||||
|    x86_cpuid(0x80000000, flags); | ||||
|    max_flag = flags[0]; | ||||
|    if (max_flag >= 0x80000001u) | ||||
|    { | ||||
|       x86_cpuid(0x80000001, flags); | ||||
|       if (flags[3] & (1 << 23)) | ||||
|          cpu |= RETRO_SIMD_MMX; | ||||
|       if (flags[3] & (1 << 22)) | ||||
|          cpu |= RETRO_SIMD_MMXEXT; | ||||
|    } | ||||
| #elif defined(__linux__) | ||||
|    if (check_arm_cpu_feature("neon")) | ||||
|    { | ||||
|       cpu |= RETRO_SIMD_NEON; | ||||
| #ifdef __ARM_NEON__ | ||||
|       arm_enable_runfast_mode(); | ||||
| #endif | ||||
|    } | ||||
| 
 | ||||
|    if (check_arm_cpu_feature("vfpv3")) | ||||
|       cpu |= RETRO_SIMD_VFPV3; | ||||
| 
 | ||||
|    if (check_arm_cpu_feature("vfpv4")) | ||||
|       cpu |= RETRO_SIMD_VFPV4; | ||||
| 
 | ||||
|    if (check_arm_cpu_feature("asimd")) | ||||
|    { | ||||
|       cpu |= RETRO_SIMD_ASIMD; | ||||
| #ifdef __ARM_NEON__ | ||||
|       cpu |= RETRO_SIMD_NEON; | ||||
|       arm_enable_runfast_mode(); | ||||
| #endif | ||||
|    } | ||||
| 
 | ||||
| #if 0 | ||||
|     check_arm_cpu_feature("swp"); | ||||
|     check_arm_cpu_feature("half"); | ||||
|     check_arm_cpu_feature("thumb"); | ||||
|     check_arm_cpu_feature("fastmult"); | ||||
|     check_arm_cpu_feature("vfp"); | ||||
|     check_arm_cpu_feature("edsp"); | ||||
|     check_arm_cpu_feature("thumbee"); | ||||
|     check_arm_cpu_feature("tls"); | ||||
|     check_arm_cpu_feature("idiva"); | ||||
|     check_arm_cpu_feature("idivt"); | ||||
| #endif | ||||
| 
 | ||||
| #elif defined(__ARM_NEON__) | ||||
|    cpu |= RETRO_SIMD_NEON; | ||||
|    arm_enable_runfast_mode(); | ||||
| #elif defined(__ALTIVEC__) | ||||
|    cpu |= RETRO_SIMD_VMX; | ||||
| #elif defined(XBOX360) | ||||
|    cpu |= RETRO_SIMD_VMX128; | ||||
| #elif defined(PSP) | ||||
|    cpu |= RETRO_SIMD_VFPU; | ||||
| #elif defined(GEKKO) | ||||
|    cpu |= RETRO_SIMD_PS; | ||||
| #endif | ||||
| 
 | ||||
|    if (cpu & RETRO_SIMD_MMX)    strlcat(buf, " MMX", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_MMXEXT) strlcat(buf, " MMXEXT", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_SSE)    strlcat(buf, " SSE", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_SSE2)   strlcat(buf, " SSE2", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_SSE3)   strlcat(buf, " SSE3", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_SSSE3)  strlcat(buf, " SSSE3", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_SSE4)   strlcat(buf, " SSE4", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_SSE42)  strlcat(buf, " SSE4.2", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_AES)    strlcat(buf, " AES", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_AVX)    strlcat(buf, " AVX", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_AVX2)   strlcat(buf, " AVX2", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_NEON)   strlcat(buf, " NEON", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_VFPV3)  strlcat(buf, " VFPv3", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_VFPV4)  strlcat(buf, " VFPv4", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_VMX)    strlcat(buf, " VMX", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_VMX128) strlcat(buf, " VMX128", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_VFPU)   strlcat(buf, " VFPU", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_PS)     strlcat(buf, " PS", sizeof(buf)); | ||||
|    if (cpu & RETRO_SIMD_ASIMD)  strlcat(buf, " ASIMD", sizeof(buf)); | ||||
| 
 | ||||
|    return cpu; | ||||
| } | ||||
|  | @ -0,0 +1,897 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (archive_file.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_MMAP | ||||
| #include <fcntl.h> | ||||
| #include <errno.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include <sys/mman.h> | ||||
| #include <sys/stat.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <compat/strl.h> | ||||
| #include <file/archive_file.h> | ||||
| #include <file/file_path.h> | ||||
| #include <streams/file_stream.h> | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <lists/string_list.h> | ||||
| #include <string/stdstring.h> | ||||
| 
 | ||||
| struct file_archive_file_data | ||||
| { | ||||
| #ifdef HAVE_MMAP | ||||
|    int fd; | ||||
| #endif | ||||
|    void *data; | ||||
|    size_t size; | ||||
| }; | ||||
| 
 | ||||
| static size_t file_archive_size(file_archive_file_data_t *data) | ||||
| { | ||||
|    if (!data) | ||||
|       return 0; | ||||
|    return data->size; | ||||
| } | ||||
| 
 | ||||
| static const uint8_t *file_archive_data(file_archive_file_data_t *data) | ||||
| { | ||||
|    if (!data) | ||||
|       return NULL; | ||||
|    return (const uint8_t*)data->data; | ||||
| } | ||||
| 
 | ||||
| #ifdef HAVE_MMAP | ||||
| /* Closes, unmaps and frees. */ | ||||
| static void file_archive_free(file_archive_file_data_t *data) | ||||
| { | ||||
|    if (!data) | ||||
|       return; | ||||
| 
 | ||||
|    if (data->data) | ||||
|       munmap(data->data, data->size); | ||||
|    if (data->fd >= 0) | ||||
|       close(data->fd); | ||||
|    free(data); | ||||
| } | ||||
| 
 | ||||
| static file_archive_file_data_t* file_archive_open(const char *path) | ||||
| { | ||||
|    file_archive_file_data_t *data = (file_archive_file_data_t*)calloc(1, sizeof(*data)); | ||||
| 
 | ||||
|    if (!data) | ||||
|       return NULL; | ||||
| 
 | ||||
|    data->fd = open(path, O_RDONLY); | ||||
| 
 | ||||
|    /* Failed to open archive. */ | ||||
|    if (data->fd < 0) | ||||
|       goto error; | ||||
| 
 | ||||
|    data->size = path_get_size(path); | ||||
|    if (!data->size) | ||||
|       return data; | ||||
| 
 | ||||
|    data->data = mmap(NULL, data->size, PROT_READ, MAP_SHARED, data->fd, 0); | ||||
|    if (data->data == MAP_FAILED) | ||||
|    { | ||||
|       data->data = NULL; | ||||
| 
 | ||||
|       /* Failed to mmap() file */ | ||||
|       goto error; | ||||
|    } | ||||
| 
 | ||||
|    return data; | ||||
| 
 | ||||
| error: | ||||
|    file_archive_free(data); | ||||
|    return NULL; | ||||
| } | ||||
| #else | ||||
| 
 | ||||
| /* Closes, unmaps and frees. */ | ||||
| static void file_archive_free(file_archive_file_data_t *data) | ||||
| { | ||||
|    if (!data) | ||||
|       return; | ||||
|    if(data->data) | ||||
|       free(data->data); | ||||
|    free(data); | ||||
| } | ||||
| 
 | ||||
| static file_archive_file_data_t* file_archive_open(const char *path) | ||||
| { | ||||
|    ssize_t ret            = -1; | ||||
|    bool read_from_file    = false; | ||||
|    file_archive_file_data_t *data = (file_archive_file_data_t*) | ||||
|       calloc(1, sizeof(*data)); | ||||
| 
 | ||||
|    if (!data) | ||||
|       return NULL; | ||||
| 
 | ||||
|    read_from_file = filestream_read_file(path, &data->data, &ret); | ||||
| 
 | ||||
|    /* Failed to open archive? */ | ||||
|    if (!read_from_file || ret < 0) | ||||
|       goto error; | ||||
| 
 | ||||
|    data->size = ret; | ||||
|    return data; | ||||
| 
 | ||||
| error: | ||||
|    file_archive_free(data); | ||||
|    return NULL; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static int file_archive_get_file_list_cb( | ||||
|       const char *path, | ||||
|       const char *valid_exts, | ||||
|       const uint8_t *cdata, | ||||
|       unsigned cmode, | ||||
|       uint32_t csize, | ||||
|       uint32_t size, | ||||
|       uint32_t checksum, | ||||
|       struct archive_extract_userdata *userdata) | ||||
| { | ||||
|    union string_list_elem_attr attr; | ||||
|    int ret                      = 0; | ||||
|    struct string_list *ext_list = NULL; | ||||
|    size_t path_len              = strlen(path); | ||||
| 
 | ||||
|    (void)cdata; | ||||
|    (void)cmode; | ||||
|    (void)csize; | ||||
|    (void)size; | ||||
|    (void)checksum; | ||||
| 
 | ||||
|    attr.i = 0; | ||||
| 
 | ||||
|    if (!path_len) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (valid_exts) | ||||
|       ext_list = string_split(valid_exts, "|"); | ||||
| 
 | ||||
|    if (ext_list) | ||||
|    { | ||||
|       const char *file_ext         = NULL; | ||||
|       /* Checks if this entry is a directory or a file. */ | ||||
|       char last_char = path[path_len-1]; | ||||
| 
 | ||||
|       /* Skip if directory. */ | ||||
|       if (last_char == '/' || last_char == '\\' ) | ||||
|          goto error; | ||||
| 
 | ||||
|       file_ext = path_get_extension(path); | ||||
| 
 | ||||
|       if (!file_ext) | ||||
|          goto error; | ||||
| 
 | ||||
|       if (!string_list_find_elem_prefix(ext_list, ".", file_ext)) | ||||
|       { | ||||
|          /* keep iterating */ | ||||
|          ret = -1; | ||||
|          goto error; | ||||
|       } | ||||
| 
 | ||||
|       attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE; | ||||
|       string_list_free(ext_list); | ||||
|    } | ||||
| 
 | ||||
|    return string_list_append(userdata->list, path, attr); | ||||
| 
 | ||||
| error: | ||||
|    string_list_free(ext_list); | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| static int file_archive_extract_cb(const char *name, const char *valid_exts, | ||||
|       const uint8_t *cdata, | ||||
|       unsigned cmode, uint32_t csize, uint32_t size, | ||||
|       uint32_t checksum, struct archive_extract_userdata *userdata) | ||||
| { | ||||
|    const char *ext                   = path_get_extension(name); | ||||
| 
 | ||||
|    /* Extract first file that matches our list. */ | ||||
|    if (ext && string_list_find_elem(userdata->ext, ext)) | ||||
|    { | ||||
|       char new_path[PATH_MAX_LENGTH]; | ||||
|       char wanted_file[PATH_MAX_LENGTH]; | ||||
|       const char *delim                 = NULL; | ||||
| 
 | ||||
|       new_path[0] = wanted_file[0]      = '\0'; | ||||
| 
 | ||||
|       if (userdata->extraction_directory) | ||||
|          fill_pathname_join(new_path, userdata->extraction_directory, | ||||
|                path_basename(name), sizeof(new_path)); | ||||
|       else | ||||
|          fill_pathname_resolve_relative(new_path, userdata->archive_path, | ||||
|                path_basename(name), sizeof(new_path)); | ||||
| 
 | ||||
|       userdata->first_extracted_file_path = strdup(new_path); | ||||
| 
 | ||||
|       delim = path_get_archive_delim(userdata->archive_path); | ||||
| 
 | ||||
|       if (delim) | ||||
|       { | ||||
|          strlcpy(wanted_file, delim + 1, sizeof(wanted_file)); | ||||
| 
 | ||||
|          if (!string_is_equal_noncase(userdata->extracted_file_path, | ||||
|                    wanted_file)) | ||||
|            return 1; /* keep searching for the right file */ | ||||
|       } | ||||
|       else | ||||
|          strlcpy(wanted_file, userdata->archive_path, sizeof(wanted_file)); | ||||
| 
 | ||||
|       if (file_archive_perform_mode(new_path, | ||||
|                 valid_exts, cdata, cmode, csize, size, | ||||
|                 0, userdata)) | ||||
|          userdata->found_file = true; | ||||
| 
 | ||||
|       return 0; | ||||
|    } | ||||
| 
 | ||||
|    return 1; | ||||
| } | ||||
| 
 | ||||
| static int file_archive_parse_file_init(file_archive_transfer_t *state, | ||||
|       const char *file) | ||||
| { | ||||
|    char path[PATH_MAX_LENGTH]; | ||||
|    char *last                 = NULL; | ||||
| 
 | ||||
|    path[0] = '\0'; | ||||
| 
 | ||||
|    strlcpy(path, file, sizeof(path)); | ||||
| 
 | ||||
|    last = (char*)path_get_archive_delim(path); | ||||
| 
 | ||||
|    if (last) | ||||
|       *last = '\0'; | ||||
| 
 | ||||
|    state->backend = file_archive_get_file_backend(path); | ||||
|    if (!state->backend) | ||||
|       return -1; | ||||
| 
 | ||||
|    state->handle = file_archive_open(path); | ||||
|    if (!state->handle) | ||||
|       return -1; | ||||
| 
 | ||||
|    state->archive_size = (int32_t)file_archive_size(state->handle); | ||||
|    state->data         = file_archive_data(state->handle); | ||||
|    state->footer       = 0; | ||||
|    state->directory    = 0; | ||||
| 
 | ||||
|    return state->backend->archive_parse_file_init(state, path); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * file_archive_decompress_data_to_file: | ||||
|  * @path                        : filename path of archive. | ||||
|  * @valid_exts                  : Valid extensions of archive to be parsed. | ||||
|  *                                If NULL, allow all. | ||||
|  * @cdata                       : input data. | ||||
|  * @csize                       : size of input data. | ||||
|  * @size                        : output file size | ||||
|  * @checksum                    : CRC32 checksum from input data. | ||||
|  * | ||||
|  * Decompress data to file. | ||||
|  * | ||||
|  * Returns: true (1) on success, otherwise false (0). | ||||
|  **/ | ||||
| static int file_archive_decompress_data_to_file( | ||||
|       file_archive_file_handle_t *handle, | ||||
|       int ret, | ||||
|       const char *path, | ||||
|       const char *valid_exts, | ||||
|       const uint8_t *cdata, | ||||
|       uint32_t csize, | ||||
|       uint32_t size, | ||||
|       uint32_t checksum) | ||||
| { | ||||
|    if (!handle || ret == -1) | ||||
|    { | ||||
|       ret = 0; | ||||
|       goto end; | ||||
|    } | ||||
| 
 | ||||
| #if 0 | ||||
|    handle->real_checksum = handle->backend->stream_crc_calculate( | ||||
|          0, handle->data, size); | ||||
|    if (handle->real_checksum != checksum) | ||||
|    { | ||||
|       /* File CRC difers from archive CRC. */ | ||||
|       printf("File CRC differs from archive CRC. File: 0x%x, Archive: 0x%x.\n", | ||||
|             (unsigned)handle->real_checksum, (unsigned)checksum); | ||||
|    } | ||||
| #endif | ||||
| 
 | ||||
|    if (!filestream_write_file(path, handle->data, size)) | ||||
|    { | ||||
|       ret = false; | ||||
|       goto end; | ||||
|    } | ||||
| 
 | ||||
| end: | ||||
| 
 | ||||
|    if (handle) | ||||
|    { | ||||
|       if (handle->backend) | ||||
|       { | ||||
|          if (handle->backend->stream_free) | ||||
|             handle->backend->stream_free(handle->stream); | ||||
|       } | ||||
| 
 | ||||
|       if (handle->data) | ||||
|          free(handle->data); | ||||
|    } | ||||
| 
 | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state) | ||||
| { | ||||
|    if (!state || !state->handle) | ||||
|       return; | ||||
| 
 | ||||
|    state->type = ARCHIVE_TRANSFER_DEINIT; | ||||
|    file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL); | ||||
| } | ||||
| 
 | ||||
| int file_archive_parse_file_iterate( | ||||
|       file_archive_transfer_t *state, | ||||
|       bool *returnerr, | ||||
|       const char *file, | ||||
|       const char *valid_exts, | ||||
|       file_archive_file_cb file_cb, | ||||
|       struct archive_extract_userdata *userdata) | ||||
| { | ||||
|    if (!state) | ||||
|       return -1; | ||||
| 
 | ||||
|    switch (state->type) | ||||
|    { | ||||
|       case ARCHIVE_TRANSFER_NONE: | ||||
|          break; | ||||
|       case ARCHIVE_TRANSFER_INIT: | ||||
|          if (file_archive_parse_file_init(state, file) == 0) | ||||
|          { | ||||
|             if (userdata) | ||||
|             { | ||||
|                userdata->context = state->stream; | ||||
|                strlcpy(userdata->archive_path, file, | ||||
|                      sizeof(userdata->archive_path)); | ||||
|             } | ||||
|             state->type = ARCHIVE_TRANSFER_ITERATE; | ||||
|          } | ||||
|          else | ||||
|             state->type = ARCHIVE_TRANSFER_DEINIT_ERROR; | ||||
|          break; | ||||
|       case ARCHIVE_TRANSFER_ITERATE: | ||||
|          if (file_archive_get_file_backend(file)) | ||||
|          { | ||||
|             const struct file_archive_file_backend *backend = | ||||
|                file_archive_get_file_backend(file); | ||||
|             int ret                                         = | ||||
|                backend->archive_parse_file_iterate_step(state, | ||||
|                   valid_exts, userdata, file_cb); | ||||
| 
 | ||||
|             if (ret != 1) | ||||
|                state->type = ARCHIVE_TRANSFER_DEINIT; | ||||
|             if (ret == -1) | ||||
|                state->type = ARCHIVE_TRANSFER_DEINIT_ERROR; | ||||
| 
 | ||||
|             /* early return to prevent deinit from never firing */ | ||||
|             return 0; | ||||
|          } | ||||
|          return -1; | ||||
|       case ARCHIVE_TRANSFER_DEINIT_ERROR: | ||||
|          *returnerr = false; | ||||
|       case ARCHIVE_TRANSFER_DEINIT: | ||||
|          if (state->handle) | ||||
|          { | ||||
|             file_archive_free(state->handle); | ||||
|             state->handle = NULL; | ||||
|          } | ||||
| 
 | ||||
|          if (state->stream && state->backend) | ||||
|          { | ||||
|             if (state->backend->stream_free) | ||||
|                state->backend->stream_free(state->stream); | ||||
| 
 | ||||
|             if (state->stream) | ||||
|                free(state->stream); | ||||
| 
 | ||||
|             state->stream = NULL; | ||||
| 
 | ||||
|             if (userdata) | ||||
|                userdata->context = NULL; | ||||
|          } | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    if (  state->type == ARCHIVE_TRANSFER_DEINIT || | ||||
|          state->type == ARCHIVE_TRANSFER_DEINIT_ERROR) | ||||
|       return -1; | ||||
| 
 | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * file_archive_walk: | ||||
|  * @file                        : filename path of archive | ||||
|  * @valid_exts                  : Valid extensions of archive to be parsed. | ||||
|  *                                If NULL, allow all. | ||||
|  * @file_cb                     : file_cb function pointer | ||||
|  * @userdata                    : userdata to pass to file_cb function pointer. | ||||
|  * | ||||
|  * Low-level file parsing. Enumerates over all files and calls | ||||
|  * file_cb with userdata. | ||||
|  * | ||||
|  * Returns: true (1) on success, otherwise false (0). | ||||
|  **/ | ||||
| static bool file_archive_walk(const char *file, const char *valid_exts, | ||||
|       file_archive_file_cb file_cb, struct archive_extract_userdata *userdata) | ||||
| { | ||||
|    file_archive_transfer_t state; | ||||
|    bool returnerr                = true; | ||||
| 
 | ||||
|    state.type                    = ARCHIVE_TRANSFER_INIT; | ||||
|    state.archive_size            = 0; | ||||
|    state.handle                  = NULL; | ||||
|    state.stream                  = NULL; | ||||
|    state.footer                  = NULL; | ||||
|    state.directory               = NULL; | ||||
|    state.data                    = NULL; | ||||
|    state.backend                 = NULL; | ||||
| 
 | ||||
|    for (;;) | ||||
|    { | ||||
|       if (file_archive_parse_file_iterate(&state, &returnerr, file, | ||||
|             valid_exts, file_cb, userdata) != 0) | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return returnerr; | ||||
| } | ||||
| 
 | ||||
| int file_archive_parse_file_progress(file_archive_transfer_t *state) | ||||
| { | ||||
|    /* FIXME: this estimate is worse than before */ | ||||
|    ptrdiff_t delta = 0; | ||||
| 
 | ||||
|    if (!state || state->archive_size == 0) | ||||
|       return 0; | ||||
| 
 | ||||
|    delta = state->directory - state->data; | ||||
| 
 | ||||
|    return (int)(delta * 100 / state->archive_size); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * file_archive_extract_file: | ||||
|  * @archive_path                    : filename path to archive. | ||||
|  * @archive_path_size               : size of archive. | ||||
|  * @valid_exts                  : valid extensions for the file. | ||||
|  * @extraction_directory        : the directory to extract temporary | ||||
|  *                                file to. | ||||
|  * | ||||
|  * Extract file from archive. If no file inside the archive is | ||||
|  * specified, the first file found will be used. | ||||
|  * | ||||
|  * Returns : true (1) on success, otherwise false (0). | ||||
|  **/ | ||||
| bool file_archive_extract_file( | ||||
|       char *archive_path, | ||||
|       size_t archive_path_size, | ||||
|       const char *valid_exts, | ||||
|       const char *extraction_directory, | ||||
|       char *out_path, size_t len) | ||||
| { | ||||
|    struct archive_extract_userdata userdata; | ||||
|    bool ret                                 = true; | ||||
|    struct string_list *list                 = string_split(valid_exts, "|"); | ||||
| 
 | ||||
|    userdata.archive_path[0]                 = '\0'; | ||||
|    userdata.first_extracted_file_path       = NULL; | ||||
|    userdata.extracted_file_path             = NULL; | ||||
|    userdata.extraction_directory            = extraction_directory; | ||||
|    userdata.archive_path_size               = archive_path_size; | ||||
|    userdata.ext                             = list; | ||||
|    userdata.list                            = NULL; | ||||
|    userdata.found_file                      = false; | ||||
|    userdata.list_only                       = false; | ||||
|    userdata.context                         = NULL; | ||||
|    userdata.archive_name[0]                 = '\0'; | ||||
|    userdata.crc                             = 0; | ||||
|    userdata.dec                             = NULL; | ||||
| 
 | ||||
|    userdata.decomp_state.opt_file           = NULL; | ||||
|    userdata.decomp_state.needle             = NULL; | ||||
|    userdata.decomp_state.size               = 0; | ||||
|    userdata.decomp_state.found              = false; | ||||
| 
 | ||||
|    if (!list) | ||||
|    { | ||||
|       ret = false; | ||||
|       goto end; | ||||
|    } | ||||
| 
 | ||||
|    if (!file_archive_walk(archive_path, valid_exts, | ||||
|             file_archive_extract_cb, &userdata)) | ||||
|    { | ||||
|       /* Parsing file archive failed. */ | ||||
|       ret = false; | ||||
|       goto end; | ||||
|    } | ||||
| 
 | ||||
|    if (!userdata.found_file) | ||||
|    { | ||||
|       /* Didn't find any file that matched valid extensions
 | ||||
|        * for libretro implementation. */ | ||||
|       ret = false; | ||||
|       goto end; | ||||
|    } | ||||
| 
 | ||||
|    if (!string_is_empty(userdata.first_extracted_file_path)) | ||||
|       strlcpy(out_path, userdata.first_extracted_file_path, len); | ||||
| 
 | ||||
| end: | ||||
|    if (userdata.first_extracted_file_path) | ||||
|       free(userdata.first_extracted_file_path); | ||||
|    if (list) | ||||
|       string_list_free(list); | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * file_archive_get_file_list: | ||||
|  * @path                        : filename path of archive | ||||
|  * | ||||
|  * Returns: string listing of files from archive on success, otherwise NULL. | ||||
|  **/ | ||||
| struct string_list *file_archive_get_file_list(const char *path, | ||||
|       const char *valid_exts) | ||||
| { | ||||
|    int ret; | ||||
|    struct archive_extract_userdata userdata; | ||||
| 
 | ||||
|    strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path)); | ||||
|    userdata.first_extracted_file_path       = NULL; | ||||
|    userdata.extracted_file_path             = NULL; | ||||
|    userdata.extraction_directory            = NULL; | ||||
|    userdata.archive_path_size               = 0; | ||||
|    userdata.ext                             = NULL; | ||||
|    userdata.list                            = string_list_new(); | ||||
|    userdata.found_file                      = false; | ||||
|    userdata.list_only                       = true; | ||||
|    userdata.context                         = NULL; | ||||
|    userdata.archive_name[0]                 = '\0'; | ||||
|    userdata.crc                             = 0; | ||||
|    userdata.dec                             = NULL; | ||||
| 
 | ||||
|    userdata.decomp_state.opt_file           = NULL; | ||||
|    userdata.decomp_state.needle             = NULL; | ||||
|    userdata.decomp_state.size               = 0; | ||||
|    userdata.decomp_state.found              = false; | ||||
| 
 | ||||
|    if (!userdata.list) | ||||
|       goto error; | ||||
| 
 | ||||
|    ret = file_archive_walk(path, valid_exts, | ||||
|          file_archive_get_file_list_cb, &userdata); | ||||
| 
 | ||||
|    if (ret <= 0) | ||||
|    { | ||||
|       if (ret != -1) | ||||
|          goto error; | ||||
|    } | ||||
| 
 | ||||
|    return userdata.list; | ||||
| 
 | ||||
| error: | ||||
|    if (userdata.list) | ||||
|       string_list_free(userdata.list); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| bool file_archive_perform_mode(const char *path, const char *valid_exts, | ||||
|       const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, | ||||
|       uint32_t crc32, struct archive_extract_userdata *userdata) | ||||
| { | ||||
|    switch (cmode) | ||||
|    { | ||||
|       case ARCHIVE_MODE_UNCOMPRESSED: | ||||
|          if (!filestream_write_file(path, cdata, size)) | ||||
|             goto error; | ||||
|          break; | ||||
| 
 | ||||
|       case ARCHIVE_MODE_COMPRESSED: | ||||
|          { | ||||
|             int ret = 0; | ||||
|             file_archive_file_handle_t handle; | ||||
| 
 | ||||
|             handle.stream        = userdata->context; | ||||
|             handle.data          = NULL; | ||||
|             handle.real_checksum = 0; | ||||
|             handle.backend       = file_archive_get_file_backend(userdata->archive_path); | ||||
| 
 | ||||
|             if (!handle.backend) | ||||
|                goto error; | ||||
| 
 | ||||
|             if (!handle.backend->stream_decompress_data_to_file_init(&handle, | ||||
|                      cdata, csize, size)) | ||||
|                goto error; | ||||
| 
 | ||||
|             do | ||||
|             { | ||||
|                ret = handle.backend->stream_decompress_data_to_file_iterate( | ||||
|                      handle.stream); | ||||
|             }while(ret == 0); | ||||
| 
 | ||||
|             if (!file_archive_decompress_data_to_file(&handle, | ||||
|                      ret, path, valid_exts, | ||||
|                      cdata, csize, size, crc32)) | ||||
|                goto error; | ||||
|          } | ||||
|          break; | ||||
|       default: | ||||
|          goto error; | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| 
 | ||||
| error: | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * file_archive_filename_split: | ||||
|  * @str              : filename to turn into a string list | ||||
|  * | ||||
|  * Creates a new string list based on filename @path, delimited by a hash (#). | ||||
|  * | ||||
|  * Returns: new string list if successful, otherwise NULL. | ||||
|  */ | ||||
| static struct string_list *file_archive_filename_split(const char *path) | ||||
| { | ||||
|    union string_list_elem_attr attr; | ||||
|    struct string_list *list = string_list_new(); | ||||
|    const char *delim        = path_get_archive_delim(path); | ||||
| 
 | ||||
|    attr.i = 0; | ||||
| 
 | ||||
|    if (delim) | ||||
|    { | ||||
|       /* add archive path to list first */ | ||||
|       if (!string_list_append_n(list, path, (unsigned)(delim - path), attr)) | ||||
|          goto error; | ||||
| 
 | ||||
|       /* now add the path within the archive */ | ||||
|       delim++; | ||||
| 
 | ||||
|       if (*delim) | ||||
|       { | ||||
|          if (!string_list_append(list, delim, attr)) | ||||
|             goto error; | ||||
|       } | ||||
|    } | ||||
|    else | ||||
|       if (!string_list_append(list, path, attr)) | ||||
|          goto error; | ||||
| 
 | ||||
|    return list; | ||||
| 
 | ||||
| error: | ||||
|    string_list_free(list); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| /* Generic compressed file loader.
 | ||||
|  * Extracts to buf, unless optional_filename != 0 | ||||
|  * Then extracts to optional_filename and leaves buf alone. | ||||
|  */ | ||||
| int file_archive_compressed_read( | ||||
|       const char * path, void **buf, | ||||
|       const char* optional_filename, ssize_t *length) | ||||
| { | ||||
|    const struct file_archive_file_backend *backend = NULL; | ||||
|    int ret                            = 0; | ||||
|    struct string_list *str_list       = file_archive_filename_split(path); | ||||
| 
 | ||||
|    /* Safety check.
 | ||||
|     * If optional_filename and optional_filename | ||||
|     * exists, we simply return 0, | ||||
|     * hoping that optional_filename is the | ||||
|     * same as requested. | ||||
|     */ | ||||
|    if (optional_filename && filestream_exists(optional_filename)) | ||||
|    { | ||||
|       *length = 0; | ||||
|       string_list_free(str_list); | ||||
|       return 1; | ||||
|    } | ||||
| 
 | ||||
|    /* We assure that there is something after the '#' symbol.
 | ||||
|     * | ||||
|     * This error condition happens for example, when | ||||
|     * path = /path/to/file.7z, or | ||||
|     * path = /path/to/file.7z# | ||||
|     */ | ||||
|    if (str_list->size <= 1) | ||||
|       goto error; | ||||
| 
 | ||||
|    backend = file_archive_get_file_backend(str_list->elems[0].data); | ||||
| 
 | ||||
|    *length = backend->compressed_file_read(str_list->elems[0].data, | ||||
|          str_list->elems[1].data, buf, optional_filename); | ||||
| 
 | ||||
|    if (*length != -1) | ||||
|       ret = 1; | ||||
| 
 | ||||
|    string_list_free(str_list); | ||||
|    return ret; | ||||
| 
 | ||||
| error: | ||||
|    /* could not extract string and substring. */ | ||||
|    string_list_free(str_list); | ||||
|    *length = 0; | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| const struct file_archive_file_backend *file_archive_get_zlib_file_backend(void) | ||||
| { | ||||
| #ifdef HAVE_ZLIB | ||||
|    return &zlib_backend; | ||||
| #else | ||||
|    return NULL; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| const struct file_archive_file_backend *file_archive_get_7z_file_backend(void) | ||||
| { | ||||
| #ifdef HAVE_7ZIP | ||||
|    return &sevenzip_backend; | ||||
| #else | ||||
|    return NULL; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| const struct file_archive_file_backend* file_archive_get_file_backend(const char *path) | ||||
| { | ||||
|    char newpath[PATH_MAX_LENGTH]; | ||||
|    const char *file_ext          = NULL; | ||||
|    char *last                    = NULL; | ||||
| 
 | ||||
|    newpath[0] = '\0'; | ||||
| 
 | ||||
|    strlcpy(newpath, path, sizeof(newpath)); | ||||
| 
 | ||||
|    last = (char*)path_get_archive_delim(newpath); | ||||
| 
 | ||||
|    if (last) | ||||
|       *last = '\0'; | ||||
| 
 | ||||
|    file_ext = path_get_extension(newpath); | ||||
| 
 | ||||
| #ifdef HAVE_7ZIP | ||||
|    if (string_is_equal_noncase(file_ext, "7z")) | ||||
|       return &sevenzip_backend; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_ZLIB | ||||
|    if (     string_is_equal_noncase(file_ext, "zip") | ||||
|          || string_is_equal_noncase(file_ext, "apk") | ||||
|       ) | ||||
|       return &zlib_backend; | ||||
| #endif | ||||
| 
 | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * file_archive_get_file_crc32: | ||||
|  * @path                         : filename path of archive | ||||
|  * | ||||
|  * Returns: CRC32 of the specified file in the archive, otherwise 0. | ||||
|  * If no path within the archive is specified, the first | ||||
|  * file found inside is used. | ||||
|  **/ | ||||
| uint32_t file_archive_get_file_crc32(const char *path) | ||||
| { | ||||
|    file_archive_transfer_t state; | ||||
|    const struct file_archive_file_backend *backend = file_archive_get_file_backend(path); | ||||
|    struct archive_extract_userdata userdata        = {{0}}; | ||||
|    bool returnerr                                  = false; | ||||
|    bool contains_compressed                        = false; | ||||
|    const char *archive_path                        = NULL; | ||||
| 
 | ||||
|    if (!backend) | ||||
|       return 0; | ||||
| 
 | ||||
|    contains_compressed = path_contains_compressed_file(path); | ||||
| 
 | ||||
|    if (contains_compressed) | ||||
|    { | ||||
|       archive_path = path_get_archive_delim(path); | ||||
| 
 | ||||
|       /* move pointer right after the delimiter to give us the path */ | ||||
|       if (archive_path) | ||||
|          archive_path += 1; | ||||
|    } | ||||
| 
 | ||||
|    state.type          = ARCHIVE_TRANSFER_INIT; | ||||
|    state.archive_size  = 0; | ||||
|    state.handle        = NULL; | ||||
|    state.stream        = NULL; | ||||
|    state.footer        = NULL; | ||||
|    state.directory     = NULL; | ||||
|    state.data          = NULL; | ||||
|    state.backend       = NULL; | ||||
| 
 | ||||
|    /* Initialize and open archive first.
 | ||||
|       Sets next state type to ITERATE. */ | ||||
|    file_archive_parse_file_iterate(&state, | ||||
|             &returnerr, path, NULL, NULL, | ||||
|             &userdata); | ||||
| 
 | ||||
|    for (;;) | ||||
|    { | ||||
|       /* Now find the first file in the archive. */ | ||||
|       if (state.type == ARCHIVE_TRANSFER_ITERATE) | ||||
|          file_archive_parse_file_iterate(&state, | ||||
|                   &returnerr, path, NULL, NULL, | ||||
|                   &userdata); | ||||
| 
 | ||||
|       /* If no path specified within archive, stop after
 | ||||
|        * finding the first file. | ||||
|        */ | ||||
|       if (!contains_compressed) | ||||
|          break; | ||||
| 
 | ||||
|       /* Stop when the right file in the archive is found. */ | ||||
|       if (archive_path) | ||||
|       { | ||||
|          if (string_is_equal(userdata.extracted_file_path, archive_path)) | ||||
|             break; | ||||
|       } | ||||
|       else | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    file_archive_parse_file_iterate_stop(&state); | ||||
| 
 | ||||
|    if (userdata.crc) | ||||
|       return userdata.crc; | ||||
| 
 | ||||
|    return 0; | ||||
| } | ||||
|  | @ -0,0 +1,512 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (archive_file_sevenzip.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <boolean.h> | ||||
| #include <file/archive_file.h> | ||||
| #include <streams/file_stream.h> | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <encodings/utf.h> | ||||
| #include <encodings/crc32.h> | ||||
| #include <string/stdstring.h> | ||||
| #include <lists/string_list.h> | ||||
| #include <file/file_path.h> | ||||
| #include <compat/strl.h> | ||||
| #include <7zip/7z.h> | ||||
| #include <7zip/7zCrc.h> | ||||
| #include <7zip/7zFile.h> | ||||
| 
 | ||||
| #define SEVENZIP_MAGIC "7z\xBC\xAF\x27\x1C" | ||||
| #define SEVENZIP_MAGIC_LEN 6 | ||||
| 
 | ||||
| /* Assume W-functions do not work below Win2K and Xbox platforms */ | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) | ||||
| #ifndef LEGACY_WIN32 | ||||
| #define LEGACY_WIN32 | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| struct sevenzip_context_t { | ||||
|    CFileInStream archiveStream; | ||||
|    CLookToRead lookStream; | ||||
|    ISzAlloc allocImp; | ||||
|    ISzAlloc allocTempImp; | ||||
|    CSzArEx db; | ||||
|    size_t temp_size; | ||||
|    uint32_t block_index; | ||||
|    uint32_t index; | ||||
|    uint32_t packIndex; | ||||
|    uint8_t *output; | ||||
|    file_archive_file_handle_t *handle; | ||||
| }; | ||||
| 
 | ||||
| static void *sevenzip_stream_alloc_impl(void *p, size_t size) | ||||
| { | ||||
|    if (size == 0) | ||||
|       return 0; | ||||
|    return malloc(size); | ||||
| } | ||||
| 
 | ||||
| static void sevenzip_stream_free_impl(void *p, void *address) | ||||
| { | ||||
|    (void)p; | ||||
|    free(address); | ||||
| } | ||||
| 
 | ||||
| static void *sevenzip_stream_alloc_tmp_impl(void *p, size_t size) | ||||
| { | ||||
|    (void)p; | ||||
|    if (size == 0) | ||||
|       return 0; | ||||
|    return malloc(size); | ||||
| } | ||||
| 
 | ||||
| static void* sevenzip_stream_new(void) | ||||
| { | ||||
|    struct sevenzip_context_t *sevenzip_context = | ||||
|          (struct sevenzip_context_t*)calloc(1, sizeof(struct sevenzip_context_t)); | ||||
| 
 | ||||
|    /* These are the allocation routines - currently using
 | ||||
|     * the non-standard 7zip choices. */ | ||||
|    sevenzip_context->allocImp.Alloc     = sevenzip_stream_alloc_impl; | ||||
|    sevenzip_context->allocImp.Free      = sevenzip_stream_free_impl; | ||||
|    sevenzip_context->allocTempImp.Alloc = sevenzip_stream_alloc_tmp_impl; | ||||
|    sevenzip_context->allocTempImp.Free  = sevenzip_stream_free_impl; | ||||
|    sevenzip_context->block_index        = 0xFFFFFFFF; | ||||
|    sevenzip_context->output             = NULL; | ||||
|    sevenzip_context->handle             = NULL; | ||||
| 
 | ||||
|    return sevenzip_context; | ||||
| } | ||||
| 
 | ||||
| static void sevenzip_stream_free(void *data) | ||||
| { | ||||
|    struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)data; | ||||
| 
 | ||||
|    if (!sevenzip_context) | ||||
|       return; | ||||
| 
 | ||||
|    if (sevenzip_context->output) | ||||
|    { | ||||
|       IAlloc_Free(&sevenzip_context->allocImp, sevenzip_context->output); | ||||
|       sevenzip_context->output       = NULL; | ||||
|       sevenzip_context->handle->data = NULL; | ||||
|    } | ||||
| 
 | ||||
|    SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp); | ||||
|    File_Close(&sevenzip_context->archiveStream.file); | ||||
| } | ||||
| 
 | ||||
| /* Extract the relative path (needle) from a 7z archive
 | ||||
|  * (path) and allocate a buf for it to write it in. | ||||
|  * If optional_outfile is set, extract to that instead | ||||
|  * and don't allocate buffer. | ||||
|  */ | ||||
| static int sevenzip_file_read( | ||||
|       const char *path, | ||||
|       const char *needle, void **buf, | ||||
|       const char *optional_outfile) | ||||
| { | ||||
|    CFileInStream archiveStream; | ||||
|    CLookToRead lookStream; | ||||
|    ISzAlloc allocImp; | ||||
|    ISzAlloc allocTempImp; | ||||
|    CSzArEx db; | ||||
|    uint8_t *output      = 0; | ||||
|    long outsize         = -1; | ||||
| 
 | ||||
|    /*These are the allocation routines.
 | ||||
|     * Currently using the non-standard 7zip choices. */ | ||||
|    allocImp.Alloc       = sevenzip_stream_alloc_impl; | ||||
|    allocImp.Free        = sevenzip_stream_free_impl; | ||||
|    allocTempImp.Alloc   = sevenzip_stream_alloc_tmp_impl; | ||||
|    allocTempImp.Free    = sevenzip_stream_free_impl; | ||||
| 
 | ||||
| #if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32) | ||||
|    if (!string_is_empty(path)) | ||||
|    { | ||||
|       wchar_t *pathW = utf8_to_utf16_string_alloc(path); | ||||
| 
 | ||||
|       if (pathW) | ||||
|       { | ||||
|          /* Could not open 7zip archive? */ | ||||
|          if (InFile_OpenW(&archiveStream.file, pathW)) | ||||
|          { | ||||
|             free(pathW); | ||||
|             return -1; | ||||
|          } | ||||
| 
 | ||||
|          free(pathW); | ||||
|       } | ||||
|    } | ||||
| #else | ||||
|    /* Could not open 7zip archive? */ | ||||
|    if (InFile_Open(&archiveStream.file, path)) | ||||
|       return -1; | ||||
| #endif | ||||
| 
 | ||||
|    FileInStream_CreateVTable(&archiveStream); | ||||
|    LookToRead_CreateVTable(&lookStream, false); | ||||
|    lookStream.realStream = &archiveStream.s; | ||||
|    LookToRead_Init(&lookStream); | ||||
|    CrcGenerateTable(); | ||||
| 
 | ||||
|    db.db.PackSizes               = NULL; | ||||
|    db.db.PackCRCsDefined         = NULL; | ||||
|    db.db.PackCRCs                = NULL; | ||||
|    db.db.Folders                 = NULL; | ||||
|    db.db.Files                   = NULL; | ||||
|    db.db.NumPackStreams          = 0; | ||||
|    db.db.NumFolders              = 0; | ||||
|    db.db.NumFiles                = 0; | ||||
|    db.startPosAfterHeader        = 0; | ||||
|    db.dataPos                    = 0; | ||||
|    db.FolderStartPackStreamIndex = NULL; | ||||
|    db.PackStreamStartPositions   = NULL; | ||||
|    db.FolderStartFileIndex       = NULL; | ||||
|    db.FileIndexToFolderIndexMap  = NULL; | ||||
|    db.FileNameOffsets            = NULL; | ||||
|    db.FileNames.data             = NULL; | ||||
|    db.FileNames.size             = 0; | ||||
| 
 | ||||
|    SzArEx_Init(&db); | ||||
| 
 | ||||
|    if (SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp) == SZ_OK) | ||||
|    { | ||||
|       uint32_t i; | ||||
|       bool file_found      = false; | ||||
|       uint16_t *temp       = NULL; | ||||
|       size_t temp_size     = 0; | ||||
|       uint32_t block_index = 0xFFFFFFFF; | ||||
|       SRes res             = SZ_OK; | ||||
| 
 | ||||
|       for (i = 0; i < db.db.NumFiles; i++) | ||||
|       { | ||||
|          size_t len; | ||||
|          char infile[PATH_MAX_LENGTH]; | ||||
|          size_t offset                = 0; | ||||
|          size_t outSizeProcessed      = 0; | ||||
|          const CSzFileItem    *f      = db.db.Files + i; | ||||
| 
 | ||||
|          /* We skip over everything which is not a directory.
 | ||||
|           * FIXME: Why continue then if f->IsDir is true?*/ | ||||
|          if (f->IsDir) | ||||
|             continue; | ||||
| 
 | ||||
|          len = SzArEx_GetFileNameUtf16(&db, i, NULL); | ||||
| 
 | ||||
|          if (len > temp_size) | ||||
|          { | ||||
|             if (temp) | ||||
|                free(temp); | ||||
|             temp_size = len; | ||||
|             temp = (uint16_t *)malloc(temp_size * sizeof(temp[0])); | ||||
| 
 | ||||
|             if (temp == 0) | ||||
|             { | ||||
|                res = SZ_ERROR_MEM; | ||||
|                break; | ||||
|             } | ||||
|          } | ||||
| 
 | ||||
|          SzArEx_GetFileNameUtf16(&db, i, temp); | ||||
|          res       = SZ_ERROR_FAIL; | ||||
|          infile[0] = '\0'; | ||||
| 
 | ||||
|          if (temp) | ||||
|             res = utf16_to_char_string(temp, infile, sizeof(infile)) | ||||
|                ? SZ_OK : SZ_ERROR_FAIL; | ||||
| 
 | ||||
|          if (string_is_equal(infile, needle)) | ||||
|          { | ||||
|             size_t output_size   = 0; | ||||
| 
 | ||||
|             /* C LZMA SDK does not support chunked extraction - see here:
 | ||||
|              * sourceforge.net/p/sevenzip/discussion/45798/thread/6fb59aaf/ | ||||
|              * */ | ||||
|             file_found = true; | ||||
|             res = SzArEx_Extract(&db, &lookStream.s, i, &block_index, | ||||
|                   &output, &output_size, &offset, &outSizeProcessed, | ||||
|                   &allocImp, &allocTempImp); | ||||
| 
 | ||||
|             if (res != SZ_OK) | ||||
|                break; /* This goes to the error section. */ | ||||
| 
 | ||||
|             outsize = outSizeProcessed; | ||||
| 
 | ||||
|             if (optional_outfile != NULL) | ||||
|             { | ||||
|                const void *ptr = (const void*)(output + offset); | ||||
| 
 | ||||
|                if (!filestream_write_file(optional_outfile, ptr, outsize)) | ||||
|                { | ||||
|                   res        = SZ_OK; | ||||
|                   file_found = true; | ||||
|                   outsize    = -1; | ||||
|                } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                /*We could either use the 7Zip allocated buffer,
 | ||||
|                 * or create our own and use it. | ||||
|                 * We would however need to realloc anyways, because RetroArch | ||||
|                 * expects a \0 at the end, therefore we allocate new, | ||||
|                 * copy and free the old one. */ | ||||
|                *buf = malloc(outsize + 1); | ||||
|                ((char*)(*buf))[outsize] = '\0'; | ||||
|                memcpy(*buf,output + offset,outsize); | ||||
|             } | ||||
|             break; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       if (temp) | ||||
|          free(temp); | ||||
|       IAlloc_Free(&allocImp, output); | ||||
| 
 | ||||
|       if (!(file_found && res == SZ_OK)) | ||||
|       { | ||||
|          /* Error handling
 | ||||
|           * | ||||
|           * Failed to open compressed file inside 7zip archive. | ||||
|           */ | ||||
| 
 | ||||
|          outsize    = -1; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    SzArEx_Free(&db, &allocImp); | ||||
|    File_Close(&archiveStream.file); | ||||
| 
 | ||||
|    return (int)outsize; | ||||
| } | ||||
| 
 | ||||
| static bool sevenzip_stream_decompress_data_to_file_init( | ||||
|       file_archive_file_handle_t *handle, | ||||
|       const uint8_t *cdata,  uint32_t csize, uint32_t size) | ||||
| { | ||||
|    struct sevenzip_context_t *sevenzip_context = | ||||
|          (struct sevenzip_context_t*)handle->stream; | ||||
| 
 | ||||
|    if (!sevenzip_context) | ||||
|       return false; | ||||
| 
 | ||||
|    sevenzip_context->handle = handle; | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static int sevenzip_stream_decompress_data_to_file_iterate(void *data) | ||||
| { | ||||
|    struct sevenzip_context_t *sevenzip_context = | ||||
|          (struct sevenzip_context_t*)data; | ||||
| 
 | ||||
|    SRes res                = SZ_ERROR_FAIL; | ||||
|    size_t output_size      = 0; | ||||
|    size_t offset           = 0; | ||||
|    size_t outSizeProcessed = 0; | ||||
| 
 | ||||
|    res = SzArEx_Extract(&sevenzip_context->db, | ||||
|          &sevenzip_context->lookStream.s, sevenzip_context->index, | ||||
|          &sevenzip_context->block_index, &sevenzip_context->output, | ||||
|          &output_size, &offset, &outSizeProcessed, | ||||
|          &sevenzip_context->allocImp, &sevenzip_context->allocTempImp); | ||||
| 
 | ||||
|    if (res != SZ_OK) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (sevenzip_context->handle) | ||||
|       sevenzip_context->handle->data = sevenzip_context->output + offset; | ||||
| 
 | ||||
|    return 1; | ||||
| } | ||||
| 
 | ||||
| static int sevenzip_parse_file_init(file_archive_transfer_t *state, | ||||
|       const char *file) | ||||
| { | ||||
|    struct sevenzip_context_t *sevenzip_context = | ||||
|          (struct sevenzip_context_t*)sevenzip_stream_new(); | ||||
| 
 | ||||
|    if (state->archive_size < SEVENZIP_MAGIC_LEN) | ||||
|       goto error; | ||||
| 
 | ||||
|    if (string_is_not_equal_fast(state->data, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN)) | ||||
|       goto error; | ||||
| 
 | ||||
|    state->stream = sevenzip_context; | ||||
| 
 | ||||
| #if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32) | ||||
|    if (!string_is_empty(file)) | ||||
|    { | ||||
|       wchar_t *fileW = utf8_to_utf16_string_alloc(file); | ||||
| 
 | ||||
|       if (fileW) | ||||
|       { | ||||
|          /* could not open 7zip archive? */ | ||||
|          if (InFile_OpenW(&sevenzip_context->archiveStream.file, fileW)) | ||||
|          { | ||||
|             free(fileW); | ||||
|             goto error; | ||||
|          } | ||||
| 
 | ||||
|          free(fileW); | ||||
|       } | ||||
|    } | ||||
| #else | ||||
|    /* could not open 7zip archive? */ | ||||
|    if (InFile_Open(&sevenzip_context->archiveStream.file, file)) | ||||
|       goto error; | ||||
| #endif | ||||
| 
 | ||||
|    FileInStream_CreateVTable(&sevenzip_context->archiveStream); | ||||
|    LookToRead_CreateVTable(&sevenzip_context->lookStream, false); | ||||
|    sevenzip_context->lookStream.realStream = &sevenzip_context->archiveStream.s; | ||||
|    LookToRead_Init(&sevenzip_context->lookStream); | ||||
|    CrcGenerateTable(); | ||||
|    SzArEx_Init(&sevenzip_context->db); | ||||
| 
 | ||||
|    if (SzArEx_Open(&sevenzip_context->db, &sevenzip_context->lookStream.s, | ||||
|          &sevenzip_context->allocImp, &sevenzip_context->allocTempImp) != SZ_OK) | ||||
|       goto error; | ||||
| 
 | ||||
|    return 0; | ||||
| 
 | ||||
| error: | ||||
|    if (sevenzip_context) | ||||
|       sevenzip_stream_free(sevenzip_context); | ||||
|    return -1; | ||||
| } | ||||
| 
 | ||||
| static int sevenzip_parse_file_iterate_step_internal( | ||||
|       file_archive_transfer_t *state, char *filename, | ||||
|       const uint8_t **cdata, unsigned *cmode, | ||||
|       uint32_t *size, uint32_t *csize, uint32_t *checksum, | ||||
|       unsigned *payback, struct archive_extract_userdata *userdata) | ||||
| { | ||||
|    struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)state->stream; | ||||
|    const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->index; | ||||
| 
 | ||||
|    if (sevenzip_context->index < sevenzip_context->db.db.NumFiles) | ||||
|    { | ||||
|       size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db, | ||||
|             sevenzip_context->index, NULL); | ||||
|       uint64_t compressed_size = 0; | ||||
| 
 | ||||
|       if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams) | ||||
|       { | ||||
|          compressed_size = sevenzip_context->db.db.PackSizes[sevenzip_context->packIndex]; | ||||
|          sevenzip_context->packIndex++; | ||||
|       } | ||||
| 
 | ||||
|       if (len < PATH_MAX_LENGTH && !file->IsDir) | ||||
|       { | ||||
|          char infile[PATH_MAX_LENGTH]; | ||||
|          SRes res                     = SZ_ERROR_FAIL; | ||||
|          uint16_t *temp               = (uint16_t*)malloc(len * sizeof(uint16_t)); | ||||
| 
 | ||||
|          if (!temp) | ||||
|             return -1; | ||||
| 
 | ||||
|          infile[0] = '\0'; | ||||
| 
 | ||||
|          SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index, | ||||
|                temp); | ||||
| 
 | ||||
|          if (temp) | ||||
|          { | ||||
|             res  = utf16_to_char_string(temp, infile, sizeof(infile)) | ||||
|                ? SZ_OK : SZ_ERROR_FAIL; | ||||
|             free(temp); | ||||
|          } | ||||
| 
 | ||||
|          if (res != SZ_OK) | ||||
|             return -1; | ||||
| 
 | ||||
|          strlcpy(filename, infile, PATH_MAX_LENGTH); | ||||
| 
 | ||||
|          *cmode    = ARCHIVE_MODE_COMPRESSED; | ||||
|          *checksum = file->Crc; | ||||
|          *size     = (uint32_t)file->Size; | ||||
|          *csize    = (uint32_t)compressed_size; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    *payback = 1; | ||||
| 
 | ||||
|    return 1; | ||||
| } | ||||
| 
 | ||||
| static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state, | ||||
|       const char *valid_exts, | ||||
|       struct archive_extract_userdata *userdata, file_archive_file_cb file_cb) | ||||
| { | ||||
|    char filename[PATH_MAX_LENGTH]; | ||||
|    const uint8_t *cdata = NULL; | ||||
|    uint32_t checksum    = 0; | ||||
|    uint32_t size        = 0; | ||||
|    uint32_t csize       = 0; | ||||
|    unsigned cmode       = 0; | ||||
|    unsigned payload     = 0; | ||||
|    struct sevenzip_context_t *sevenzip_context = NULL; | ||||
|    int ret; | ||||
| 
 | ||||
|    filename[0]                   = '\0'; | ||||
| 
 | ||||
|    ret = sevenzip_parse_file_iterate_step_internal(state, filename, | ||||
|          &cdata, &cmode, &size, &csize, | ||||
|          &checksum, &payload, userdata); | ||||
| 
 | ||||
|    if (ret != 1) | ||||
|       return ret; | ||||
| 
 | ||||
|    userdata->extracted_file_path = filename; | ||||
|    userdata->crc                 = checksum; | ||||
| 
 | ||||
|    if (file_cb && !file_cb(filename, valid_exts, cdata, cmode, | ||||
|             csize, size, checksum, userdata)) | ||||
|       return 0; | ||||
| 
 | ||||
|    sevenzip_context = (struct sevenzip_context_t*)state->stream; | ||||
| 
 | ||||
|    sevenzip_context->index += payload; | ||||
| 
 | ||||
|    return 1; | ||||
| } | ||||
| 
 | ||||
| static uint32_t sevenzip_stream_crc32_calculate(uint32_t crc, | ||||
|       const uint8_t *data, size_t length) | ||||
| { | ||||
|    return encoding_crc32(crc, data, length); | ||||
| } | ||||
| 
 | ||||
| const struct file_archive_file_backend sevenzip_backend = { | ||||
|    sevenzip_stream_new, | ||||
|    sevenzip_stream_free, | ||||
|    sevenzip_stream_decompress_data_to_file_init, | ||||
|    sevenzip_stream_decompress_data_to_file_iterate, | ||||
|    sevenzip_stream_crc32_calculate, | ||||
|    sevenzip_file_read, | ||||
|    sevenzip_parse_file_init, | ||||
|    sevenzip_parse_file_iterate_step, | ||||
|    "7z" | ||||
| }; | ||||
|  | @ -0,0 +1,384 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (archive_file_zlib.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <file/archive_file.h> | ||||
| #include <streams/file_stream.h> | ||||
| #include <streams/trans_stream.h> | ||||
| #include <retro_inline.h> | ||||
| #include <retro_miscellaneous.h> | ||||
| #include <encodings/crc32.h> | ||||
| 
 | ||||
| /* Only for MAX_WBITS */ | ||||
| #include <compat/zlib.h> | ||||
| 
 | ||||
| #ifndef CENTRAL_FILE_HEADER_SIGNATURE | ||||
| #define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef END_OF_CENTRAL_DIR_SIGNATURE | ||||
| #define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50 | ||||
| #endif | ||||
| 
 | ||||
| static INLINE uint32_t read_le(const uint8_t *data, unsigned size) | ||||
| { | ||||
|    unsigned i; | ||||
|    uint32_t val = 0; | ||||
| 
 | ||||
|    size *= 8; | ||||
|    for (i = 0; i < size; i += 8) | ||||
|       val |= (uint32_t)*data++ << i; | ||||
| 
 | ||||
|    return val; | ||||
| } | ||||
| 
 | ||||
| static void *zlib_stream_new(void) | ||||
| { | ||||
|    return zlib_inflate_backend.stream_new(); | ||||
| } | ||||
| 
 | ||||
| static void zlib_stream_free(void *stream) | ||||
| { | ||||
|    zlib_inflate_backend.stream_free(stream); | ||||
| } | ||||
| 
 | ||||
| static bool zlib_stream_decompress_data_to_file_init( | ||||
|       file_archive_file_handle_t *handle, | ||||
|       const uint8_t *cdata,  uint32_t csize, uint32_t size) | ||||
| { | ||||
|    if (!handle) | ||||
|       return false; | ||||
| 
 | ||||
|    handle->stream = zlib_inflate_backend.stream_new(); | ||||
| 
 | ||||
|    if (!handle->stream) | ||||
|       goto error; | ||||
| 
 | ||||
|    if (zlib_inflate_backend.define) | ||||
|       zlib_inflate_backend.define(handle->stream, "window_bits", (uint32_t)-MAX_WBITS); | ||||
| 
 | ||||
|    handle->data = (uint8_t*)malloc(size); | ||||
| 
 | ||||
|    if (!handle->data) | ||||
|       goto error; | ||||
| 
 | ||||
|    zlib_inflate_backend.set_in(handle->stream, | ||||
|          (const uint8_t*)cdata, csize); | ||||
|    zlib_inflate_backend.set_out(handle->stream, | ||||
|          handle->data, size); | ||||
| 
 | ||||
|    return true; | ||||
| 
 | ||||
| error: | ||||
|    if (handle->stream) | ||||
|       zlib_inflate_backend.stream_free(handle->stream); | ||||
|    if (handle->data) | ||||
|       free(handle->data); | ||||
| 
 | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| static int zlib_stream_decompress_data_to_file_iterate(void *stream) | ||||
| { | ||||
|    bool zstatus; | ||||
|    uint32_t rd, wn; | ||||
|    enum trans_stream_error terror; | ||||
| 
 | ||||
|    if (!stream) | ||||
|       return -1; | ||||
| 
 | ||||
|    zstatus = zlib_inflate_backend.trans(stream, false, &rd, &wn, &terror); | ||||
| 
 | ||||
|    if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL) | ||||
|       return -1; | ||||
| 
 | ||||
|    if (zstatus && !terror) | ||||
|       return 1; | ||||
| 
 | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| static uint32_t zlib_stream_crc32_calculate(uint32_t crc, | ||||
|       const uint8_t *data, size_t length) | ||||
| { | ||||
|    return encoding_crc32(crc, data, length); | ||||
| } | ||||
| 
 | ||||
| static bool zip_file_decompressed_handle( | ||||
|       file_archive_file_handle_t *handle, | ||||
|       const uint8_t *cdata, uint32_t csize, | ||||
|       uint32_t size, uint32_t crc32) | ||||
| { | ||||
|    int ret   = 0; | ||||
| 
 | ||||
|    handle->backend = &zlib_backend; | ||||
| 
 | ||||
|    if (!handle->backend->stream_decompress_data_to_file_init( | ||||
|             handle, cdata, csize, size)) | ||||
|       return false; | ||||
| 
 | ||||
|    do | ||||
|    { | ||||
|       ret = handle->backend->stream_decompress_data_to_file_iterate( | ||||
|             handle->stream); | ||||
|    }while(ret == 0); | ||||
| 
 | ||||
| #if 0 | ||||
|    handle->real_checksum = handle->backend->stream_crc_calculate(0, | ||||
|          handle->data, size); | ||||
| 
 | ||||
|    if (handle->real_checksum != crc32) | ||||
|       goto error; | ||||
| #endif | ||||
| 
 | ||||
|    if (handle->stream) | ||||
|       free(handle->stream); | ||||
| 
 | ||||
|    return true; | ||||
| #if 0 | ||||
| error: | ||||
|    if (handle->stream) | ||||
|       free(handle->stream); | ||||
|    if (handle->data) | ||||
|       free(handle->data); | ||||
| 
 | ||||
|    handle->stream = NULL; | ||||
|    handle->data   = NULL; | ||||
|    return false; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* Extract the relative path (needle) from a
 | ||||
|  * ZIP archive (path) and allocate a buffer for it to write it in. | ||||
|  * | ||||
|  * optional_outfile if not NULL will be used to extract the file to. | ||||
|  * buf will be 0 then. | ||||
|  */ | ||||
| 
 | ||||
| static int zip_file_decompressed( | ||||
|       const char *name, const char *valid_exts, | ||||
|       const uint8_t *cdata, unsigned cmode, | ||||
|       uint32_t csize, uint32_t size, | ||||
|       uint32_t crc32, struct archive_extract_userdata *userdata) | ||||
| { | ||||
|    /* Ignore directories. */ | ||||
|    if (name[strlen(name) - 1] == '/' || name[strlen(name) - 1] == '\\') | ||||
|       return 1; | ||||
| 
 | ||||
|    if (strstr(name, userdata->decomp_state.needle)) | ||||
|    { | ||||
|       bool goto_error = false; | ||||
|       file_archive_file_handle_t handle = {0}; | ||||
| 
 | ||||
|       userdata->decomp_state.found = true; | ||||
| 
 | ||||
|       if (zip_file_decompressed_handle(&handle, | ||||
|                cdata, csize, size, crc32)) | ||||
|       { | ||||
|          if (userdata->decomp_state.opt_file != 0) | ||||
|          { | ||||
|             /* Called in case core has need_fullpath enabled. */ | ||||
|             char *buf       = (char*)malloc(size); | ||||
| 
 | ||||
|             if (buf) | ||||
|             { | ||||
|                memcpy(buf, handle.data, size); | ||||
| 
 | ||||
|                if (!filestream_write_file(userdata->decomp_state.opt_file, buf, size)) | ||||
|                   goto_error = true; | ||||
|             } | ||||
| 
 | ||||
|             free(buf); | ||||
| 
 | ||||
|             userdata->decomp_state.size = 0; | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             /* Called in case core has need_fullpath disabled.
 | ||||
|              * Will copy decompressed content directly into | ||||
|              * RetroArch's ROM buffer. */ | ||||
|             *userdata->decomp_state.buf = malloc(size); | ||||
|             memcpy(*userdata->decomp_state.buf, handle.data, size); | ||||
| 
 | ||||
|             userdata->decomp_state.size = size; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       if (handle.data) | ||||
|          free(handle.data); | ||||
| 
 | ||||
|       if (goto_error) | ||||
|          return 0; | ||||
|    } | ||||
| 
 | ||||
|    return 1; | ||||
| } | ||||
| 
 | ||||
| static int zip_file_read( | ||||
|       const char *path, | ||||
|       const char *needle, void **buf, | ||||
|       const char *optional_outfile) | ||||
| { | ||||
|    file_archive_transfer_t zlib; | ||||
|    struct archive_extract_userdata userdata = {{0}}; | ||||
|    bool returnerr                    = true; | ||||
|    int ret                           = 0; | ||||
| 
 | ||||
|    zlib.type                         = ARCHIVE_TRANSFER_INIT; | ||||
| 
 | ||||
|    userdata.decomp_state.needle      = NULL; | ||||
|    userdata.decomp_state.opt_file    = NULL; | ||||
|    userdata.decomp_state.found       = false; | ||||
|    userdata.decomp_state.buf         = buf; | ||||
| 
 | ||||
|    if (needle) | ||||
|       userdata.decomp_state.needle   = strdup(needle); | ||||
|    if (optional_outfile) | ||||
|       userdata.decomp_state.opt_file = strdup(optional_outfile); | ||||
| 
 | ||||
|    do | ||||
|    { | ||||
|       ret = file_archive_parse_file_iterate(&zlib, &returnerr, path, | ||||
|             "", zip_file_decompressed, &userdata); | ||||
|       if (!returnerr) | ||||
|          break; | ||||
|    }while(ret == 0 && !userdata.decomp_state.found); | ||||
| 
 | ||||
|    file_archive_parse_file_iterate_stop(&zlib); | ||||
| 
 | ||||
|    if (userdata.decomp_state.opt_file) | ||||
|       free(userdata.decomp_state.opt_file); | ||||
|    if (userdata.decomp_state.needle) | ||||
|       free(userdata.decomp_state.needle); | ||||
| 
 | ||||
|    if (!userdata.decomp_state.found) | ||||
|       return -1; | ||||
| 
 | ||||
|    return (int)userdata.decomp_state.size; | ||||
| } | ||||
| 
 | ||||
| static int zip_parse_file_init(file_archive_transfer_t *state, | ||||
|       const char *file) | ||||
| { | ||||
|    if (state->archive_size < 22) | ||||
|       return -1; | ||||
| 
 | ||||
|    state->footer = state->data + state->archive_size - 22; | ||||
| 
 | ||||
|    for (;; state->footer--) | ||||
|    { | ||||
|       if (state->footer <= state->data + 22) | ||||
|          return -1; | ||||
|       if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE) | ||||
|       { | ||||
|          unsigned comment_len = read_le(state->footer + 20, 2); | ||||
|          if (state->footer + 22 + comment_len == state->data + state->archive_size) | ||||
|             break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    state->directory = state->data + read_le(state->footer + 16, 4); | ||||
| 
 | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| static int zip_parse_file_iterate_step_internal( | ||||
|       file_archive_transfer_t *state, char *filename, | ||||
|       const uint8_t **cdata, | ||||
|       unsigned *cmode, uint32_t *size, uint32_t *csize, | ||||
|       uint32_t *checksum, unsigned *payback) | ||||
| { | ||||
|    uint32_t offset; | ||||
|    uint32_t namelength, extralength, commentlength, | ||||
|             offsetNL, offsetEL; | ||||
|    uint32_t signature = read_le(state->directory + 0, 4); | ||||
| 
 | ||||
|    if (signature != CENTRAL_FILE_HEADER_SIGNATURE) | ||||
|       return 0; | ||||
| 
 | ||||
|    *cmode         = read_le(state->directory + 10, 2); /* compression mode, 0 = store, 8 = deflate */ | ||||
|    *checksum      = read_le(state->directory + 16, 4); /* CRC32 */ | ||||
|    *csize         = read_le(state->directory + 20, 4); /* compressed size */ | ||||
|    *size          = read_le(state->directory + 24, 4); /* uncompressed size */ | ||||
| 
 | ||||
|    namelength     = read_le(state->directory + 28, 2); /* file name length */ | ||||
|    extralength    = read_le(state->directory + 30, 2); /* extra field length */ | ||||
|    commentlength  = read_le(state->directory + 32, 2); /* file comment length */ | ||||
| 
 | ||||
|    if (namelength >= PATH_MAX_LENGTH) | ||||
|       return -1; | ||||
| 
 | ||||
|    memcpy(filename, state->directory + 46, namelength); /* file name */ | ||||
| 
 | ||||
|    offset         = read_le(state->directory + 42, 4); /* relative offset of local file header */ | ||||
|    offsetNL       = read_le(state->data + offset + 26, 2); /* file name length */ | ||||
|    offsetEL       = read_le(state->data + offset + 28, 2); /* extra field length */ | ||||
| 
 | ||||
|    *cdata         = state->data + offset + 30 + offsetNL + offsetEL; | ||||
| 
 | ||||
|    *payback       = 46 + namelength + extralength + commentlength; | ||||
| 
 | ||||
|    return 1; | ||||
| } | ||||
| 
 | ||||
| static int zip_parse_file_iterate_step(file_archive_transfer_t *state, | ||||
|       const char *valid_exts, struct archive_extract_userdata *userdata, | ||||
|       file_archive_file_cb file_cb) | ||||
| { | ||||
|    char filename[PATH_MAX_LENGTH] = {0}; | ||||
|    const uint8_t *cdata           = NULL; | ||||
|    uint32_t checksum              = 0; | ||||
|    uint32_t size                  = 0; | ||||
|    uint32_t csize                 = 0; | ||||
|    unsigned cmode                 = 0; | ||||
|    unsigned payload               = 0; | ||||
|    int ret                        = zip_parse_file_iterate_step_internal( | ||||
|          state, filename, &cdata, &cmode, &size, &csize, &checksum, &payload); | ||||
| 
 | ||||
|    if (ret != 1) | ||||
|       return ret; | ||||
| 
 | ||||
|    userdata->extracted_file_path = filename; | ||||
|    userdata->crc = checksum; | ||||
| 
 | ||||
|    if (file_cb && !file_cb(filename, valid_exts, cdata, cmode, | ||||
|             csize, size, checksum, userdata)) | ||||
|       return 0; | ||||
| 
 | ||||
|    state->directory += payload; | ||||
| 
 | ||||
|    return 1; | ||||
| } | ||||
| 
 | ||||
| const struct file_archive_file_backend zlib_backend = { | ||||
|    zlib_stream_new, | ||||
|    zlib_stream_free, | ||||
|    zlib_stream_decompress_data_to_file_init, | ||||
|    zlib_stream_decompress_data_to_file_iterate, | ||||
|    zlib_stream_crc32_calculate, | ||||
|    zip_file_read, | ||||
|    zip_parse_file_init, | ||||
|    zip_parse_file_iterate_step, | ||||
|    "zlib" | ||||
| }; | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,149 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (config_file_userdata.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <file/file_path.h> | ||||
| #include <lists/string_list.h> | ||||
| 
 | ||||
| #include <file/config_file_userdata.h> | ||||
| 
 | ||||
| int config_userdata_get_float(void *userdata, const char *key_str, | ||||
|       float *value, float default_value) | ||||
| { | ||||
|    bool got; | ||||
|    char key[2][256]; | ||||
|    struct config_file_userdata *usr = (struct config_file_userdata*)userdata; | ||||
| 
 | ||||
|    fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); | ||||
|    fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); | ||||
| 
 | ||||
|    got = config_get_float  (usr->conf, key[0], value); | ||||
|    got = got || config_get_float(usr->conf, key[1], value); | ||||
| 
 | ||||
|    if (!got) | ||||
|       *value = default_value; | ||||
|    return got; | ||||
| } | ||||
| 
 | ||||
| int config_userdata_get_int(void *userdata, const char *key_str, | ||||
|       int *value, int default_value) | ||||
| { | ||||
|    bool got; | ||||
|    char key[2][256]; | ||||
|    struct config_file_userdata *usr = (struct config_file_userdata*)userdata; | ||||
| 
 | ||||
|    fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); | ||||
|    fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); | ||||
| 
 | ||||
|    got = config_get_int  (usr->conf, key[0], value); | ||||
|    got = got || config_get_int(usr->conf, key[1], value); | ||||
| 
 | ||||
|    if (!got) | ||||
|       *value = default_value; | ||||
|    return got; | ||||
| } | ||||
| 
 | ||||
| int config_userdata_get_float_array(void *userdata, const char *key_str, | ||||
|       float **values, unsigned *out_num_values, | ||||
|       const float *default_values, unsigned num_default_values) | ||||
| { | ||||
|    char key[2][256]; | ||||
|    struct config_file_userdata *usr = (struct config_file_userdata*)userdata; | ||||
|    char *str = NULL; | ||||
| 
 | ||||
|    fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); | ||||
|    fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); | ||||
| 
 | ||||
|    if (  config_get_string(usr->conf, key[0], &str) || | ||||
|          config_get_string(usr->conf, key[1], &str)) | ||||
|    { | ||||
|       unsigned i; | ||||
|       struct string_list *list = string_split(str, " "); | ||||
|       *values = (float*)calloc(list->size, sizeof(float)); | ||||
|       for (i = 0; i < list->size; i++) | ||||
|          (*values)[i] = (float)strtod(list->elems[i].data, NULL); | ||||
|       *out_num_values = (unsigned)list->size; | ||||
|       string_list_free(list); | ||||
|       free(str); | ||||
|       return true; | ||||
|    } | ||||
| 
 | ||||
|    *values = (float*)calloc(num_default_values, sizeof(float)); | ||||
|    memcpy(*values, default_values, sizeof(float) * num_default_values); | ||||
|    *out_num_values = num_default_values; | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| int config_userdata_get_int_array(void *userdata, const char *key_str, | ||||
|       int **values, unsigned *out_num_values, | ||||
|       const int *default_values, unsigned num_default_values) | ||||
| { | ||||
|    char key[2][256]; | ||||
|    struct config_file_userdata *usr = (struct config_file_userdata*)userdata; | ||||
|    char *str = NULL; | ||||
|    fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); | ||||
|    fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); | ||||
| 
 | ||||
|    if (  config_get_string(usr->conf, key[0], &str) || | ||||
|          config_get_string(usr->conf, key[1], &str)) | ||||
|    { | ||||
|       unsigned i; | ||||
|       struct string_list *list = string_split(str, " "); | ||||
|       *values = (int*)calloc(list->size, sizeof(int)); | ||||
|       for (i = 0; i < list->size; i++) | ||||
|          (*values)[i] = (int)strtod(list->elems[i].data, NULL); | ||||
|       *out_num_values = (unsigned)list->size; | ||||
|       string_list_free(list); | ||||
|       free(str); | ||||
|       return true; | ||||
|    } | ||||
| 
 | ||||
|    *values = (int*)calloc(num_default_values, sizeof(int)); | ||||
|    memcpy(*values, default_values, sizeof(int) * num_default_values); | ||||
|    *out_num_values = num_default_values; | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| int config_userdata_get_string(void *userdata, const char *key_str, | ||||
|       char **output, const char *default_output) | ||||
| { | ||||
|    char key[2][256]; | ||||
|    struct config_file_userdata *usr = (struct config_file_userdata*)userdata; | ||||
|    char *str = NULL; | ||||
|    fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); | ||||
|    fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); | ||||
| 
 | ||||
|    if (  config_get_string(usr->conf, key[0], &str) || | ||||
|          config_get_string(usr->conf, key[1], &str)) | ||||
|    { | ||||
|       *output = str; | ||||
|       return true; | ||||
|    } | ||||
| 
 | ||||
|    *output = strdup(default_output); | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| void config_userdata_free(void *ptr) | ||||
| { | ||||
|    if (ptr) | ||||
|       free(ptr); | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,85 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (nbio_intf.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #include <file/nbio.h> | ||||
| 
 | ||||
| extern nbio_intf_t nbio_linux; | ||||
| extern nbio_intf_t nbio_mmap_unix; | ||||
| extern nbio_intf_t nbio_mmap_win32; | ||||
| extern nbio_intf_t nbio_stdio; | ||||
| 
 | ||||
| #if defined(_linux__) | ||||
| static nbio_intf_t *internal_nbio = &nbio_linux; | ||||
| #elif defined(HAVE_MMAP) && defined(BSD) | ||||
| static nbio_intf_t *internal_nbio = &nbio_mmap_unix; | ||||
| #elif defined(_WIN32) && !defined(_XBOX) | ||||
| static nbio_intf_t *internal_nbio = &nbio_mmap_win32; | ||||
| #else | ||||
| static nbio_intf_t *internal_nbio = &nbio_stdio; | ||||
| #endif | ||||
| 
 | ||||
| void *nbio_open(const char * filename, unsigned mode) | ||||
| { | ||||
|    return internal_nbio->open(filename, mode); | ||||
| } | ||||
| 
 | ||||
| void nbio_begin_read(void *data) | ||||
| { | ||||
|    internal_nbio->begin_read(data); | ||||
| } | ||||
| 
 | ||||
| void nbio_begin_write(void *data) | ||||
| { | ||||
|    internal_nbio->begin_write(data); | ||||
| } | ||||
| 
 | ||||
| bool nbio_iterate(void *data) | ||||
| { | ||||
|    return internal_nbio->iterate(data); | ||||
| } | ||||
| 
 | ||||
| void nbio_resize(void *data, size_t len) | ||||
| { | ||||
|    internal_nbio->resize(data, len); | ||||
| } | ||||
| 
 | ||||
| void *nbio_get_ptr(void *data, size_t* len) | ||||
| { | ||||
|    return internal_nbio->get_ptr(data, len); | ||||
| } | ||||
| 
 | ||||
| void nbio_cancel(void *data) | ||||
| { | ||||
|    internal_nbio->cancel(data); | ||||
| } | ||||
| 
 | ||||
| void nbio_free(void *data) | ||||
| { | ||||
|    internal_nbio->free(data); | ||||
| } | ||||
|  | @ -0,0 +1,245 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (nbio_linux.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <file/nbio.h> | ||||
| 
 | ||||
| #if defined(__linux__) | ||||
| 
 | ||||
| #ifndef _GNU_SOURCE | ||||
| #define _GNU_SOURCE | ||||
| #endif | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| 
 | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <sys/syscall.h> | ||||
| #include <linux/aio_abi.h> | ||||
| 
 | ||||
| struct nbio_linux_t | ||||
| { | ||||
|    int fd; | ||||
|    bool busy; | ||||
| 
 | ||||
|    aio_context_t ctx; | ||||
|    struct iocb cb; | ||||
| 
 | ||||
|    void* ptr; | ||||
|    size_t len; | ||||
| }; | ||||
| 
 | ||||
| /* there's also a Unix AIO thingy, but it's not in glibc
 | ||||
|  * and we don't want more dependencies */ | ||||
| 
 | ||||
| static int io_setup(unsigned nr, aio_context_t * ctxp) | ||||
| { | ||||
|    return syscall(__NR_io_setup, nr, ctxp); | ||||
| } | ||||
| 
 | ||||
| static int io_destroy(aio_context_t ctx) | ||||
| { | ||||
|    return syscall(__NR_io_destroy, ctx); | ||||
| } | ||||
| 
 | ||||
| static int io_submit(aio_context_t ctx, long nr, struct iocb ** cbp) | ||||
| { | ||||
|    return syscall(__NR_io_submit, ctx, nr, cbp); | ||||
| } | ||||
| 
 | ||||
| static int io_cancel(aio_context_t ctx, struct iocb * iocb, struct io_event * result) | ||||
| { | ||||
|    return syscall(__NR_io_cancel, ctx, iocb, result); | ||||
| } | ||||
| 
 | ||||
| static int io_getevents(aio_context_t ctx, long min_nr, long nr, | ||||
|       struct io_event * events, struct timespec * timeout) | ||||
| { | ||||
|    return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout); | ||||
| } | ||||
| 
 | ||||
| static void nbio_begin_op(struct nbio_linux_t* handle, uint16_t op) | ||||
| { | ||||
|    struct iocb * cbp         = &handle->cb; | ||||
| 
 | ||||
|    memset(&handle->cb, 0, sizeof(handle->cb)); | ||||
| 
 | ||||
|    handle->cb.aio_fildes     = handle->fd; | ||||
|    handle->cb.aio_lio_opcode = op; | ||||
| 
 | ||||
|    handle->cb.aio_buf        = (uint64_t)(uintptr_t)handle->ptr; | ||||
|    handle->cb.aio_offset     = 0; | ||||
|    handle->cb.aio_nbytes     = handle->len; | ||||
| 
 | ||||
|    if (io_submit(handle->ctx, 1, &cbp) != 1) | ||||
|    { | ||||
|       puts("ERROR - io_submit() failed"); | ||||
|       abort(); | ||||
|    } | ||||
| 
 | ||||
|    handle->busy = true; | ||||
| } | ||||
| 
 | ||||
| static void *nbio_linux_open(const char * filename, unsigned mode) | ||||
| { | ||||
|    static const int o_flags[]  =   { O_RDONLY, O_RDWR|O_CREAT|O_TRUNC, O_RDWR, O_RDONLY, O_RDWR|O_CREAT|O_TRUNC }; | ||||
| 
 | ||||
|    aio_context_t ctx           = 0; | ||||
|    struct nbio_linux_t* handle = NULL; | ||||
|    int fd                      = open(filename, o_flags[mode]|O_CLOEXEC, 0644); | ||||
|    if (fd < 0) | ||||
|       return NULL; | ||||
| 
 | ||||
|    if (io_setup(128, &ctx) < 0) | ||||
|    { | ||||
|       close(fd); | ||||
|       return NULL; | ||||
|    } | ||||
| 
 | ||||
|    handle       = (struct nbio_linux_t*)malloc(sizeof(struct nbio_linux_t)); | ||||
|    handle->fd   = fd; | ||||
|    handle->ctx  = ctx; | ||||
|    handle->len  = lseek(fd, 0, SEEK_END); | ||||
|    handle->ptr  = malloc(handle->len); | ||||
|    handle->busy = false; | ||||
| 
 | ||||
|    return handle; | ||||
| } | ||||
| 
 | ||||
| static void nbio_linux_begin_read(void *data) | ||||
| { | ||||
|    struct nbio_linux_t* handle = (struct nbio_linux_t*)data; | ||||
|    if (handle) | ||||
|       nbio_begin_op(handle, IOCB_CMD_PREAD); | ||||
| } | ||||
| 
 | ||||
| static void nbio_linux_begin_write(void *data) | ||||
| { | ||||
|    struct nbio_linux_t* handle = (struct nbio_linux_t*)data; | ||||
|    if (handle) | ||||
|       nbio_begin_op(handle, IOCB_CMD_PWRITE); | ||||
| } | ||||
| 
 | ||||
| static bool nbio_linux_iterate(void *data) | ||||
| { | ||||
|    struct nbio_linux_t* handle = (struct nbio_linux_t*)data; | ||||
|    if (!handle) | ||||
|       return false; | ||||
|    if (handle->busy) | ||||
|    { | ||||
|       struct io_event ev; | ||||
|       if (io_getevents(handle->ctx, 0, 1, &ev, NULL) == 1) | ||||
|          handle->busy = false; | ||||
|    } | ||||
|    return !handle->busy; | ||||
| } | ||||
| 
 | ||||
| static void nbio_linux_resize(void *data, size_t len) | ||||
| { | ||||
|    struct nbio_linux_t* handle = (struct nbio_linux_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
| 
 | ||||
|    if (len < handle->len) | ||||
|    { | ||||
|       /* this works perfectly fine if this check is removed, but it
 | ||||
|        * won't work on other nbio implementations */ | ||||
|       /* therefore, it's blocked so nobody accidentally relies on it */ | ||||
|       puts("ERROR - attempted file shrink operation, not implemented"); | ||||
|       abort(); | ||||
|    } | ||||
| 
 | ||||
|    if (ftruncate(handle->fd, len) != 0) | ||||
|    { | ||||
|       puts("ERROR - couldn't resize file (ftruncate)"); | ||||
|       abort(); /* this one returns void and I can't find any other way
 | ||||
|                   for it to report failure */ | ||||
|    } | ||||
|    handle->ptr = realloc(handle->ptr, len); | ||||
|    handle->len = len; | ||||
| } | ||||
| 
 | ||||
| static void *nbio_linux_get_ptr(void *data, size_t* len) | ||||
| { | ||||
|    struct nbio_linux_t* handle = (struct nbio_linux_t*)data; | ||||
|    if (!handle) | ||||
|       return NULL; | ||||
|    if (len) | ||||
|       *len = handle->len; | ||||
|    if (!handle->busy) | ||||
|       return handle->ptr; | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static void nbio_linux_cancel(void *data) | ||||
| { | ||||
|    struct nbio_linux_t* handle = (struct nbio_linux_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
| 
 | ||||
|    if (handle->busy) | ||||
|    { | ||||
|       struct io_event ev; | ||||
|       io_cancel(handle->ctx, &handle->cb, &ev); | ||||
|       handle->busy = false; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void nbio_linux_free(void *data) | ||||
| { | ||||
|    struct nbio_linux_t* handle = (struct nbio_linux_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
| 
 | ||||
|    io_destroy(handle->ctx); | ||||
|    close(handle->fd); | ||||
|    free(handle->ptr); | ||||
|    free(handle); | ||||
| } | ||||
| 
 | ||||
| nbio_intf_t nbio_linux = { | ||||
|    nbio_linux_open, | ||||
|    nbio_linux_begin_read, | ||||
|    nbio_linux_begin_write, | ||||
|    nbio_linux_iterate, | ||||
|    nbio_linux_resize, | ||||
|    nbio_linux_get_ptr, | ||||
|    nbio_linux_cancel, | ||||
|    nbio_linux_free, | ||||
|    "nbio_linux", | ||||
| }; | ||||
| #else | ||||
| nbio_intf_t nbio_linux = { | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    "nbio_linux", | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,268 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (nbio_stdio.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <file/nbio.h> | ||||
| #include <encodings/utf.h> | ||||
| 
 | ||||
| /* Assume W-functions do not work below Win2K and Xbox platforms */ | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) | ||||
| 
 | ||||
| #ifndef LEGACY_WIN32 | ||||
| #define LEGACY_WIN32 | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| struct nbio_stdio_t | ||||
| { | ||||
|    FILE* f; | ||||
|    void* data; | ||||
|    size_t progress; | ||||
|    size_t len; | ||||
|    /*
 | ||||
|     * possible values: | ||||
|     * NBIO_READ, NBIO_WRITE - obvious | ||||
|     * -1 - currently doing nothing | ||||
|     * -2 - the pointer was reallocated since the last operation | ||||
|     */ | ||||
|    signed char op; | ||||
|    signed char mode; | ||||
| }; | ||||
| 
 | ||||
| #if !defined(_WIN32) || defined(LEGACY_WIN32) | ||||
| static const char    *stdio_modes[] = { "rb", "wb", "r+b", "rb", "wb", "r+b" }; | ||||
| #else | ||||
| static const wchar_t *stdio_modes[] = { L"rb", L"wb", L"r+b", L"rb", L"wb", L"r+b" }; | ||||
| #endif | ||||
| 
 | ||||
| static void *nbio_stdio_open(const char * filename, unsigned mode) | ||||
| { | ||||
|    void *buf                   = NULL; | ||||
|    struct nbio_stdio_t* handle = NULL; | ||||
|    size_t len                  = 0; | ||||
| #if !defined(_WIN32) || defined(LEGACY_WIN32) | ||||
|    FILE* f                     = fopen(filename, stdio_modes[mode]); | ||||
| #else | ||||
|    wchar_t *filename_wide      = utf8_to_utf16_string_alloc(filename); | ||||
|    FILE* f                     = _wfopen(filename_wide, stdio_modes[mode]); | ||||
| 
 | ||||
|    if (filename_wide) | ||||
|       free(filename_wide); | ||||
| #endif | ||||
|    if (!f) | ||||
|       return NULL; | ||||
| 
 | ||||
|    handle                = (struct nbio_stdio_t*)malloc(sizeof(struct nbio_stdio_t)); | ||||
| 
 | ||||
|    if (!handle) | ||||
|       goto error; | ||||
| 
 | ||||
|    handle->f             = f; | ||||
| 
 | ||||
|    switch (mode) | ||||
|    { | ||||
|       case NBIO_WRITE: | ||||
|       case BIO_WRITE: | ||||
|          break; | ||||
|       default: | ||||
|          fseek(handle->f, 0, SEEK_END); | ||||
|          len = ftell(handle->f); | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    handle->mode          = mode; | ||||
| 
 | ||||
|    if (len) | ||||
|       buf                = malloc(len); | ||||
| 
 | ||||
|    if (len && !buf) | ||||
|       goto error; | ||||
| 
 | ||||
|    handle->data          = buf; | ||||
|    handle->len           = len; | ||||
|    handle->progress      = handle->len; | ||||
|    handle->op            = -2; | ||||
| 
 | ||||
|    return handle; | ||||
| 
 | ||||
| error: | ||||
|    if (handle) | ||||
|       free(handle); | ||||
|    fclose(f); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static void nbio_stdio_begin_read(void *data) | ||||
| { | ||||
|    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
| 
 | ||||
|    if (handle->op >= 0) | ||||
|    { | ||||
|       puts("ERROR - attempted file read operation while busy"); | ||||
|       abort(); | ||||
|    } | ||||
| 
 | ||||
|    fseek(handle->f, 0, SEEK_SET); | ||||
| 
 | ||||
|    handle->op       = NBIO_READ; | ||||
|    handle->progress = 0; | ||||
| } | ||||
| 
 | ||||
| static void nbio_stdio_begin_write(void *data) | ||||
| { | ||||
|    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
| 
 | ||||
|    if (handle->op >= 0) | ||||
|    { | ||||
|       puts("ERROR - attempted file write operation while busy"); | ||||
|       abort(); | ||||
|    } | ||||
| 
 | ||||
|    fseek(handle->f, 0, SEEK_SET); | ||||
|    handle->op = NBIO_WRITE; | ||||
|    handle->progress = 0; | ||||
| } | ||||
| 
 | ||||
| static bool nbio_stdio_iterate(void *data) | ||||
| { | ||||
|    size_t amount               = 65536; | ||||
|    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; | ||||
| 
 | ||||
|    if (!handle) | ||||
|       return false; | ||||
| 
 | ||||
|    if (amount > handle->len - handle->progress) | ||||
|       amount = handle->len - handle->progress; | ||||
| 
 | ||||
|    switch (handle->op) | ||||
|    { | ||||
|       case NBIO_READ: | ||||
|          if (handle->mode == BIO_READ) | ||||
|          { | ||||
|             amount = handle->len; | ||||
|             fread((char*)handle->data, 1, amount, handle->f); | ||||
|          } | ||||
|          else | ||||
|             fread((char*)handle->data + handle->progress, 1, amount, handle->f); | ||||
|          break; | ||||
|       case NBIO_WRITE: | ||||
|          if (handle->mode == BIO_WRITE) | ||||
|          { | ||||
|             size_t written = 0; | ||||
|             amount = handle->len; | ||||
|             written = fwrite((char*)handle->data, 1, amount, handle->f); | ||||
|             if (written != amount) | ||||
|                return false; | ||||
|          } | ||||
|          else | ||||
|             fwrite((char*)handle->data + handle->progress, 1, amount, handle->f); | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    handle->progress += amount; | ||||
| 
 | ||||
|    if (handle->progress == handle->len) | ||||
|       handle->op = -1; | ||||
|    return (handle->op < 0); | ||||
| } | ||||
| 
 | ||||
| static void nbio_stdio_resize(void *data, size_t len) | ||||
| { | ||||
|    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
| 
 | ||||
|    if (handle->op >= 0) | ||||
|    { | ||||
|       puts("ERROR - attempted file resize operation while busy"); | ||||
|       abort(); | ||||
|    } | ||||
|    if (len < handle->len) | ||||
|    { | ||||
|       puts("ERROR - attempted file shrink operation, not implemented"); | ||||
|       abort(); | ||||
|    } | ||||
| 
 | ||||
|    handle->len  = len; | ||||
|    handle->data = realloc(handle->data, handle->len); | ||||
|    handle->op   = -1; | ||||
|    handle->progress = handle->len; | ||||
| } | ||||
| 
 | ||||
| static void *nbio_stdio_get_ptr(void *data, size_t* len) | ||||
| { | ||||
|    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; | ||||
|    if (!handle) | ||||
|       return NULL; | ||||
|    if (len) | ||||
|       *len = handle->len; | ||||
|    if (handle->op == -1) | ||||
|       return handle->data; | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static void nbio_stdio_cancel(void *data) | ||||
| { | ||||
|    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
| 
 | ||||
|    handle->op = -1; | ||||
|    handle->progress = handle->len; | ||||
| } | ||||
| 
 | ||||
| static void nbio_stdio_free(void *data) | ||||
| { | ||||
|    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
|    if (handle->op >= 0) | ||||
|    { | ||||
|       puts("ERROR - attempted free() while busy"); | ||||
|       abort(); | ||||
|    } | ||||
|    fclose(handle->f); | ||||
|    free(handle->data); | ||||
| 
 | ||||
|    handle->f    = NULL; | ||||
|    handle->data = NULL; | ||||
|    free(handle); | ||||
| } | ||||
| 
 | ||||
| nbio_intf_t nbio_stdio = { | ||||
|    nbio_stdio_open, | ||||
|    nbio_stdio_begin_read, | ||||
|    nbio_stdio_begin_write, | ||||
|    nbio_stdio_iterate, | ||||
|    nbio_stdio_resize, | ||||
|    nbio_stdio_get_ptr, | ||||
|    nbio_stdio_cancel, | ||||
|    nbio_stdio_free, | ||||
|    "nbio_stdio", | ||||
| }; | ||||
|  | @ -0,0 +1,191 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (nbio_unixmmap.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #include <file/nbio.h> | ||||
| 
 | ||||
| #if defined(HAVE_MMAP) && defined(BSD) | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <direct.h> | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| #include <fcntl.h> | ||||
| #include <sys/mman.h> | ||||
| 
 | ||||
| #ifdef __APPLE__ | ||||
| 
 | ||||
| #ifndef O_CLOEXEC | ||||
| #define O_CLOEXEC 0x1000000 | ||||
| #endif | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| #ifndef O_CLOEXEC | ||||
| #define O_CLOEXEC 0 | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| struct nbio_mmap_unix_t | ||||
| { | ||||
|    int fd; | ||||
|    int map_flags; | ||||
|    size_t len; | ||||
|    void* ptr; | ||||
| }; | ||||
| 
 | ||||
| static void *nbio_mmap_unix_open(const char * filename, unsigned mode) | ||||
| { | ||||
|    static const int o_flags[] =   { O_RDONLY,  O_RDWR|O_CREAT|O_TRUNC, O_RDWR,               O_RDONLY,  O_RDWR|O_CREAT|O_TRUNC }; | ||||
|    static const int map_flags[] = { PROT_READ, PROT_WRITE|PROT_READ,   PROT_WRITE|PROT_READ, PROT_READ, PROT_WRITE|PROT_READ   }; | ||||
| 
 | ||||
|    size_t len; | ||||
|    void* ptr                       = NULL; | ||||
|    struct nbio_mmap_unix_t* handle = NULL; | ||||
|    int fd                          = open(filename, o_flags[mode]|O_CLOEXEC, 0644); | ||||
|    if (fd < 0) | ||||
|       return NULL; | ||||
| 
 | ||||
|    len = lseek(fd, 0, SEEK_END); | ||||
|    if (len != 0) | ||||
|       ptr = mmap(NULL, len, map_flags[mode], MAP_SHARED, fd, 0); | ||||
| 
 | ||||
|    if (ptr == MAP_FAILED) | ||||
|    { | ||||
|       close(fd); | ||||
|       return NULL; | ||||
|    } | ||||
| 
 | ||||
|    handle            = malloc(sizeof(struct nbio_mmap_unix_t)); | ||||
|    handle->fd        = fd; | ||||
|    handle->map_flags = map_flags[mode]; | ||||
|    handle->len       = len; | ||||
|    handle->ptr       = ptr; | ||||
|    return handle; | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_unix_begin_read(void *data) | ||||
| { | ||||
|    /* not needed */ | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_unix_begin_write(void *data) | ||||
| { | ||||
|    /* not needed */ | ||||
| } | ||||
| 
 | ||||
| static bool nbio_mmap_unix_iterate(void *data) | ||||
| { | ||||
|    return true; /* not needed */ | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_unix_resize(void *data, size_t len) | ||||
| { | ||||
|    struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
|    if (len < handle->len) | ||||
|    { | ||||
|       /* this works perfectly fine if this check is removed, but it
 | ||||
|        * won't work on other nbio implementations */ | ||||
|       /* therefore, it's blocked so nobody accidentally relies on it */ | ||||
|       puts("ERROR - attempted file shrink operation, not implemented"); | ||||
|       abort(); | ||||
|    } | ||||
| 
 | ||||
|    if (ftruncate(handle->fd, len) != 0) | ||||
|    { | ||||
|       puts("ERROR - couldn't resize file (ftruncate)"); | ||||
|       abort(); /* this one returns void and I can't find any other
 | ||||
|                   way for it to report failure */ | ||||
|    } | ||||
| 
 | ||||
|    munmap(handle->ptr, handle->len); | ||||
| 
 | ||||
|    handle->ptr = mmap(NULL, len, handle->map_flags, MAP_SHARED, handle->fd, 0); | ||||
|    handle->len = len; | ||||
| 
 | ||||
|    if (handle->ptr == MAP_FAILED) | ||||
|    { | ||||
|       puts("ERROR - couldn't resize file (mmap)"); | ||||
|       abort(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *nbio_mmap_unix_get_ptr(void *data, size_t* len) | ||||
| { | ||||
|    struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data; | ||||
|    if (!handle) | ||||
|       return NULL; | ||||
|    if (len) | ||||
|       *len = handle->len; | ||||
|    return handle->ptr; | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_unix_cancel(void *data) | ||||
| { | ||||
|    /* not needed */ | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_unix_free(void *data) | ||||
| { | ||||
|    struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
|    close(handle->fd); | ||||
|    munmap(handle->ptr, handle->len); | ||||
|    free(handle); | ||||
| } | ||||
| 
 | ||||
| nbio_intf_t nbio_mmap_unix = { | ||||
|    nbio_mmap_unix_open, | ||||
|    nbio_mmap_unix_begin_read, | ||||
|    nbio_mmap_unix_begin_write, | ||||
|    nbio_mmap_unix_iterate, | ||||
|    nbio_mmap_unix_resize, | ||||
|    nbio_mmap_unix_get_ptr, | ||||
|    nbio_mmap_unix_cancel, | ||||
|    nbio_mmap_unix_free, | ||||
|    "nbio_mmap_unix", | ||||
| }; | ||||
| #else | ||||
| nbio_intf_t nbio_mmap_unix = { | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    "nbio_mmap_unix", | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,224 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (nbio_windowsmmap.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <file/nbio.h> | ||||
| 
 | ||||
| #if defined(_WIN32) && !defined(_XBOX) | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <encodings/utf.h> | ||||
| 
 | ||||
| #include <windows.h> | ||||
| 
 | ||||
| /* Assume W-functions do not work below Win2K and Xbox platforms */ | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) | ||||
| 
 | ||||
| #ifndef LEGACY_WIN32 | ||||
| #define LEGACY_WIN32 | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef FILE_SHARE_ALL | ||||
| #define FILE_SHARE_ALL (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE) | ||||
| #endif | ||||
| 
 | ||||
| struct nbio_mmap_win32_t | ||||
| { | ||||
|    HANDLE file; | ||||
|    bool is_write; | ||||
|    size_t len; | ||||
|    void* ptr; | ||||
| }; | ||||
| 
 | ||||
| static void *nbio_mmap_win32_open(const char * filename, unsigned mode) | ||||
| { | ||||
|    static const DWORD dispositions[] = { OPEN_EXISTING, CREATE_ALWAYS, OPEN_ALWAYS, OPEN_EXISTING, CREATE_ALWAYS }; | ||||
|    HANDLE mem; | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 | ||||
|    LARGE_INTEGER len; | ||||
| #else | ||||
|    SIZE_T len; | ||||
| #endif | ||||
|    struct nbio_mmap_win32_t* handle  = NULL; | ||||
|    void* ptr                         = NULL; | ||||
|    bool is_write                     = (mode == NBIO_WRITE || mode == NBIO_UPDATE || mode == BIO_WRITE); | ||||
|    DWORD access                      = (is_write ? GENERIC_READ|GENERIC_WRITE : GENERIC_READ); | ||||
| #if !defined(_WIN32) || defined(LEGACY_WIN32) | ||||
|    HANDLE file                       = CreateFile(filename, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL); | ||||
| #else | ||||
|    wchar_t *filename_wide            = utf8_to_utf16_string_alloc(filename); | ||||
|    HANDLE file                       = CreateFileW(filename_wide, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL); | ||||
| 
 | ||||
|    if (filename_wide) | ||||
|       free(filename_wide); | ||||
| #endif | ||||
| 
 | ||||
|    if (file == INVALID_HANDLE_VALUE) | ||||
|       return NULL; | ||||
| 
 | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 | ||||
|    /* GetFileSizeEx is new for Windows 2000 */ | ||||
|    GetFileSizeEx(file, &len); | ||||
|    mem = CreateFileMapping(file, NULL, is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL); | ||||
|    ptr = MapViewOfFile(mem, is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len.QuadPart); | ||||
| #else | ||||
|    GetFileSize(file, &len); | ||||
|    mem = CreateFileMapping(file, NULL, is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL); | ||||
|    ptr = MapViewOfFile(mem, is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len); | ||||
| #endif | ||||
| 
 | ||||
|    CloseHandle(mem); | ||||
| 
 | ||||
|    handle           = (struct nbio_mmap_win32_t*)malloc(sizeof(struct nbio_mmap_win32_t)); | ||||
| 
 | ||||
|    handle->file     = file; | ||||
|    handle->is_write = is_write; | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 | ||||
|    handle->len      = len.QuadPart; | ||||
| #else | ||||
|    handle->len      = len; | ||||
| #endif | ||||
|    handle->ptr      = ptr; | ||||
| 
 | ||||
|    return handle; | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_win32_begin_read(void *data) | ||||
| { | ||||
|    /* not needed */ | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_win32_begin_write(void *data) | ||||
| { | ||||
|    /* not needed */ | ||||
| } | ||||
| 
 | ||||
| static bool nbio_mmap_win32_iterate(void *data) | ||||
| { | ||||
|    /* not needed */ | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_win32_resize(void *data, size_t len) | ||||
| { | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 | ||||
|    LARGE_INTEGER len_li; | ||||
| #else | ||||
|    SIZE_T len_li; | ||||
| #endif | ||||
|    HANDLE mem; | ||||
|    struct nbio_mmap_win32_t* handle  = (struct nbio_mmap_win32_t*)data; | ||||
| 
 | ||||
|    if (!handle) | ||||
|       return; | ||||
| 
 | ||||
|    if (len < handle->len) | ||||
|    { | ||||
|       /* this works perfectly fine if this check is removed,
 | ||||
|        * but it won't work on other nbio implementations */ | ||||
|       /* therefore, it's blocked so nobody accidentally
 | ||||
|        * relies on it. */ | ||||
|       puts("ERROR - attempted file shrink operation, not implemented"); | ||||
|       abort(); | ||||
|    } | ||||
| 
 | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 | ||||
|    /* SetFilePointerEx is new for Windows 2000 */ | ||||
|    len_li.QuadPart = len; | ||||
|    SetFilePointerEx(handle->file, len_li, NULL, FILE_BEGIN); | ||||
| #else | ||||
|    len_li = len; | ||||
|    SetFilePointer(handle->file, len_li, NULL, FILE_BEGIN); | ||||
| #endif | ||||
| 
 | ||||
|    if (!SetEndOfFile(handle->file)) | ||||
|    { | ||||
|       puts("ERROR - couldn't resize file (SetEndOfFile)"); | ||||
|       abort(); /* this one returns void and I can't find any other way for it to report failure */ | ||||
|    } | ||||
|    handle->len = len; | ||||
| 
 | ||||
|    UnmapViewOfFile(handle->ptr); | ||||
|    mem = CreateFileMapping(handle->file, NULL, handle->is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL); | ||||
|    handle->ptr = MapViewOfFile(mem, handle->is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len); | ||||
|    CloseHandle(mem); | ||||
| 
 | ||||
|    if (!handle->ptr) | ||||
|    { | ||||
|       puts("ERROR - couldn't resize file (MapViewOfFile)"); | ||||
|       abort(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void *nbio_mmap_win32_get_ptr(void *data, size_t* len) | ||||
| { | ||||
|    struct nbio_mmap_win32_t* handle  = (struct nbio_mmap_win32_t*)data; | ||||
|    if (!handle) | ||||
|       return NULL; | ||||
|    if (len) | ||||
|       *len = handle->len; | ||||
|    return handle->ptr; | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_win32_cancel(void *data) | ||||
| { | ||||
|    /* not needed */ | ||||
| } | ||||
| 
 | ||||
| static void nbio_mmap_win32_free(void *data) | ||||
| { | ||||
|    struct nbio_mmap_win32_t* handle  = (struct nbio_mmap_win32_t*)data; | ||||
|    if (!handle) | ||||
|       return; | ||||
|    CloseHandle(handle->file); | ||||
|    UnmapViewOfFile(handle->ptr); | ||||
|    free(handle); | ||||
| } | ||||
| 
 | ||||
| nbio_intf_t nbio_mmap_win32 = { | ||||
|    nbio_mmap_win32_open, | ||||
|    nbio_mmap_win32_begin_read, | ||||
|    nbio_mmap_win32_begin_write, | ||||
|    nbio_mmap_win32_iterate, | ||||
|    nbio_mmap_win32_resize, | ||||
|    nbio_mmap_win32_get_ptr, | ||||
|    nbio_mmap_win32_cancel, | ||||
|    nbio_mmap_win32_free, | ||||
|    "nbio_mmap_win32", | ||||
| }; | ||||
| #else | ||||
| nbio_intf_t nbio_mmap_win32 = { | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    NULL, | ||||
|    "nbio_mmap_win32", | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,294 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (retro_dirent.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_common.h> | ||||
| 
 | ||||
| #include <boolean.h> | ||||
| #include <retro_dirent.h> | ||||
| #include <encodings/utf.h> | ||||
| #include <compat/strl.h> | ||||
| #include <string/stdstring.h> | ||||
| 
 | ||||
| #if defined(_WIN32) | ||||
| #  ifdef _MSC_VER | ||||
| #    define setmode _setmode | ||||
| #  endif | ||||
| #include <sys/stat.h> | ||||
| #  ifdef _XBOX | ||||
| #    include <xtl.h> | ||||
| #    define INVALID_FILE_ATTRIBUTES -1 | ||||
| #  else | ||||
| #    include <io.h> | ||||
| #    include <fcntl.h> | ||||
| #    include <direct.h> | ||||
| #    include <windows.h> | ||||
| #  endif | ||||
| #elif defined(VITA) | ||||
| #  include <psp2/io/fcntl.h> | ||||
| #  include <psp2/io/dirent.h> | ||||
| #include <psp2/io/stat.h> | ||||
| #else | ||||
| #  if defined(PSP) | ||||
| #    include <pspiofilemgr.h> | ||||
| #  endif | ||||
| #  include <sys/types.h> | ||||
| #  include <sys/stat.h> | ||||
| #  include <dirent.h> | ||||
| #  include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __CELLOS_LV2__ | ||||
| #include <cell/cell_fs.h> | ||||
| #endif | ||||
| 
 | ||||
| #if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) | ||||
| #include <unistd.h> /* stat() is defined here */ | ||||
| #endif | ||||
| 
 | ||||
| #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) | ||||
| #ifndef LEGACY_WIN32 | ||||
| #define LEGACY_WIN32 | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| struct RDIR | ||||
| { | ||||
| #if defined(_WIN32) | ||||
| #if defined(LEGACY_WIN32) | ||||
|    WIN32_FIND_DATA entry; | ||||
| #else | ||||
|    WIN32_FIND_DATAW entry; | ||||
| #endif | ||||
|    HANDLE directory; | ||||
|    bool next; | ||||
|    char path[PATH_MAX_LENGTH]; | ||||
| #elif defined(VITA) || defined(PSP) | ||||
|    SceUID directory; | ||||
|    SceIoDirent entry; | ||||
| #elif defined(__CELLOS_LV2__) | ||||
|    CellFsErrno error; | ||||
|    int directory; | ||||
|    CellFsDirent entry; | ||||
| #else | ||||
|    DIR *directory; | ||||
|    const struct dirent *entry; | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| struct RDIR *retro_opendir(const char *name) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|    char path_buf[1024]; | ||||
|    char *path_local   = NULL; | ||||
|    wchar_t *path_wide = NULL; | ||||
|    unsigned path_len; | ||||
| #endif | ||||
|    struct RDIR *rdir  = (struct RDIR*)calloc(1, sizeof(*rdir)); | ||||
| 
 | ||||
|    if (!rdir) | ||||
|       return NULL; | ||||
| 
 | ||||
| #if defined(_WIN32) | ||||
|    (void)path_wide; | ||||
|    (void)path_local; | ||||
| 
 | ||||
|    path_buf[0] = '\0'; | ||||
|    path_len = strlen(name); | ||||
| 
 | ||||
|    /* Non-NT platforms don't like extra slashes in the path */ | ||||
|    if (name[path_len - 1] == '\\') | ||||
|       snprintf(path_buf, sizeof(path_buf), "%s*", name); | ||||
|    else | ||||
|       snprintf(path_buf, sizeof(path_buf), "%s\\*", name); | ||||
| 
 | ||||
| #if defined(LEGACY_WIN32) | ||||
|    path_local      = utf8_to_local_string_alloc(path_buf); | ||||
|    rdir->directory = FindFirstFile(path_local, &rdir->entry); | ||||
| 
 | ||||
|    if (path_local) | ||||
|       free(path_local); | ||||
| #else | ||||
|    path_wide       = utf8_to_utf16_string_alloc(path_buf); | ||||
|    rdir->directory = FindFirstFileW(path_wide, &rdir->entry); | ||||
| 
 | ||||
|    if (path_wide) | ||||
|       free(path_wide); | ||||
| #endif | ||||
| 
 | ||||
| #elif defined(VITA) || defined(PSP) | ||||
|    rdir->directory = sceIoDopen(name); | ||||
| #elif defined(_3DS) | ||||
|    rdir->directory = !string_is_empty(name) ? opendir(name) : NULL; | ||||
|    rdir->entry     = NULL; | ||||
| #elif defined(__CELLOS_LV2__) | ||||
|    rdir->error     = cellFsOpendir(name, &rdir->directory); | ||||
| #else | ||||
|    rdir->directory = opendir(name); | ||||
|    rdir->entry     = NULL; | ||||
| #endif | ||||
| 
 | ||||
|    if (rdir->directory) | ||||
|       return rdir; | ||||
| 
 | ||||
|    free(rdir); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| bool retro_dirent_error(struct RDIR *rdir) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|    return (rdir->directory == INVALID_HANDLE_VALUE); | ||||
| #elif defined(VITA) || defined(PSP) | ||||
|    return (rdir->directory < 0); | ||||
| #elif defined(__CELLOS_LV2__) | ||||
|    return (rdir->error != CELL_FS_SUCCEEDED); | ||||
| #else | ||||
|    return !(rdir->directory); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| int retro_readdir(struct RDIR *rdir) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|    if(rdir->next) | ||||
| #if defined(LEGACY_WIN32) | ||||
|       return (FindNextFile(rdir->directory, &rdir->entry) != 0); | ||||
| #else | ||||
|       return (FindNextFileW(rdir->directory, &rdir->entry) != 0); | ||||
| #endif | ||||
| 
 | ||||
|    rdir->next = true; | ||||
|    return (rdir->directory != INVALID_HANDLE_VALUE); | ||||
| #elif defined(VITA) || defined(PSP) | ||||
|    return (sceIoDread(rdir->directory, &rdir->entry) > 0); | ||||
| #elif defined(__CELLOS_LV2__) | ||||
|    uint64_t nread; | ||||
|    rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread); | ||||
|    return (nread != 0); | ||||
| #else | ||||
|    return ((rdir->entry = readdir(rdir->directory)) != NULL); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| const char *retro_dirent_get_name(struct RDIR *rdir) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
| #if defined(LEGACY_WIN32) | ||||
|    char *name_local = local_to_utf8_string_alloc(rdir->entry.cFileName); | ||||
|    memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName)); | ||||
|    strlcpy(rdir->entry.cFileName, name_local, sizeof(rdir->entry.cFileName)); | ||||
| 
 | ||||
|    if (name_local) | ||||
|       free(name_local); | ||||
| #else | ||||
|    char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName); | ||||
|    memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName)); | ||||
|    strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName)); | ||||
| 
 | ||||
|    if (name) | ||||
|       free(name); | ||||
| #endif | ||||
|    return (char*)rdir->entry.cFileName; | ||||
| #elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) | ||||
|    return rdir->entry.d_name; | ||||
| #else | ||||
| 
 | ||||
|    return rdir->entry->d_name; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * | ||||
|  * retro_dirent_is_dir: | ||||
|  * @rdir         : pointer to the directory entry. | ||||
|  * @path         : path to the directory entry. | ||||
|  * | ||||
|  * Is the directory listing entry a directory? | ||||
|  * | ||||
|  * Returns: true if directory listing entry is | ||||
|  * a directory, false if not. | ||||
|  */ | ||||
| bool retro_dirent_is_dir(struct RDIR *rdir, const char *path) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|    const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry; | ||||
|    return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; | ||||
| #elif defined(PSP) || defined(VITA) | ||||
|    const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry; | ||||
| #if defined(PSP) | ||||
|    return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR; | ||||
| #elif defined(VITA) | ||||
|    return SCE_S_ISDIR(entry->d_stat.st_mode); | ||||
| #endif | ||||
| #elif defined(__CELLOS_LV2__) | ||||
|    CellFsDirent *entry = (CellFsDirent*)&rdir->entry; | ||||
|    return (entry->d_type == CELL_FS_TYPE_DIRECTORY); | ||||
| #else | ||||
|    struct stat buf; | ||||
| #if defined(DT_DIR) | ||||
|    const struct dirent *entry = (const struct dirent*)rdir->entry; | ||||
|    if (entry->d_type == DT_DIR) | ||||
|       return true; | ||||
|    /* This can happen on certain file systems. */ | ||||
|    if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)) | ||||
|       return false; | ||||
| #endif | ||||
|    /* dirent struct doesn't have d_type, do it the slow way ... */ | ||||
|    if (stat(path, &buf) < 0) | ||||
|       return false; | ||||
|    return S_ISDIR(buf.st_mode); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void retro_dirent_include_hidden(struct RDIR *rdir, bool include_hidden) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|    if (include_hidden) | ||||
|       rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; | ||||
|    else | ||||
|       rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void retro_closedir(struct RDIR *rdir) | ||||
| { | ||||
|    if (!rdir) | ||||
|       return; | ||||
| 
 | ||||
| #if defined(_WIN32) | ||||
|    if (rdir->directory != INVALID_HANDLE_VALUE) | ||||
|       FindClose(rdir->directory); | ||||
| #elif defined(VITA) || defined(PSP) | ||||
|    sceIoDclose(rdir->directory); | ||||
| #elif defined(__CELLOS_LV2__) | ||||
|    rdir->error = cellFsClosedir(rdir->directory); | ||||
| #else | ||||
|    if (rdir->directory) | ||||
|       closedir(rdir->directory); | ||||
| #endif | ||||
| 
 | ||||
|    free(rdir); | ||||
| } | ||||
|  | @ -0,0 +1,623 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (rbmp.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| /* Modified version of stb_image's BMP sources. */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <stdarg.h> | ||||
| #include <stddef.h> /* ptrdiff_t on osx */ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| 
 | ||||
| #include <formats/image.h> | ||||
| #include <formats/rbmp.h> | ||||
| 
 | ||||
| /* truncate int to byte without warnings */ | ||||
| #define RBMP__BYTECAST(x)  ((unsigned char) ((x) & 255)) | ||||
| 
 | ||||
| #define RBMP_COMPUTE_Y(r, g, b) ((unsigned char) ((((r) * 77) + ((g) * 150) +  (29 * (b))) >> 8)) | ||||
| 
 | ||||
| typedef struct | ||||
| { | ||||
|    uint32_t img_x; | ||||
|    uint32_t img_y; | ||||
|    int img_n; | ||||
|    int img_out_n; | ||||
| 
 | ||||
|    int buflen; | ||||
|    unsigned char buffer_start[128]; | ||||
| 
 | ||||
|    unsigned char *img_buffer; | ||||
|    unsigned char *img_buffer_end; | ||||
|    unsigned char *img_buffer_original; | ||||
| } rbmp__context; | ||||
| 
 | ||||
| struct rbmp | ||||
| { | ||||
|    uint8_t *buff_data; | ||||
|    uint32_t *output_image; | ||||
| }; | ||||
| 
 | ||||
| static INLINE unsigned char rbmp__get8(rbmp__context *s) | ||||
| { | ||||
|    if (s->img_buffer < s->img_buffer_end) | ||||
|       return *s->img_buffer++; | ||||
| 
 | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| static void rbmp__skip(rbmp__context *s, int n) | ||||
| { | ||||
|    if (n < 0) | ||||
|    { | ||||
|       s->img_buffer = s->img_buffer_end; | ||||
|       return; | ||||
|    } | ||||
| 
 | ||||
|    s->img_buffer += n; | ||||
| } | ||||
| 
 | ||||
| static int rbmp__get16le(rbmp__context *s) | ||||
| { | ||||
|    int z = rbmp__get8(s); | ||||
|    return z + (rbmp__get8(s) << 8); | ||||
| } | ||||
| 
 | ||||
| static uint32_t rbmp__get32le(rbmp__context *s) | ||||
| { | ||||
|    uint32_t z = rbmp__get16le(s); | ||||
|    return z + (rbmp__get16le(s) << 16); | ||||
| } | ||||
| 
 | ||||
| static unsigned char *rbmp__convert_format( | ||||
|       unsigned char *data, | ||||
|       int img_n, | ||||
|       int req_comp, | ||||
|       unsigned int x, | ||||
|       unsigned int y) | ||||
| { | ||||
|    int i,j; | ||||
|    unsigned char *good = (unsigned char *)malloc(req_comp * x * y); | ||||
| 
 | ||||
|    if (!good) | ||||
|       return NULL; | ||||
| 
 | ||||
|    for (j=0; j < (int) y; ++j) | ||||
|    { | ||||
|       unsigned char *src  = data + j * x * img_n   ; | ||||
|       unsigned char *dest = good + j * x * req_comp; | ||||
| 
 | ||||
|       switch (((img_n)*8+(req_comp))) | ||||
|       { | ||||
|          case ((1)*8+(2)): | ||||
|             for(i=x-1; i >= 0; --i, src += 1, dest += 2) | ||||
|                dest[0]=src[0], dest[1]=255; | ||||
|             break; | ||||
|          case ((1)*8+(3)): | ||||
|             for(i=x-1; i >= 0; --i, src += 1, dest += 3) | ||||
|                dest[0]=dest[1]=dest[2]=src[0]; | ||||
|             break; | ||||
|          case ((1)*8+(4)): | ||||
|             for(i=x-1; i >= 0; --i, src += 1, dest += 4) | ||||
|                dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; | ||||
|             break; | ||||
|          case ((2)*8+(1)): | ||||
|             for(i=x-1; i >= 0; --i, src += 2, dest += 1) | ||||
|                dest[0]=src[0]; | ||||
|             break; | ||||
|          case ((2)*8+(3)): | ||||
|             for(i=x-1; i >= 0; --i, src += 2, dest += 3) | ||||
|                dest[0]=dest[1]=dest[2]=src[0]; | ||||
|             break; | ||||
|          case ((2)*8+(4)): | ||||
|             for(i=x-1; i >= 0; --i, src += 2, dest += 4) | ||||
|                dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; | ||||
|             break; | ||||
|          case ((3)*8+(4)): | ||||
|             for(i=x-1; i >= 0; --i, src += 3, dest += 4) | ||||
|                dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; | ||||
|             break; | ||||
|          case ((3)*8+(1)): | ||||
|             for(i=x-1; i >= 0; --i, src += 3, dest += 1) | ||||
|                dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]); | ||||
|             break; | ||||
|          case ((3)*8+(2)): | ||||
|             for(i=x-1; i >= 0; --i, src += 3, dest += 2) | ||||
|                dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = 255; | ||||
|             break; | ||||
|          case ((4)*8+(1)): | ||||
|             for(i=x-1; i >= 0; --i, src += 4, dest += 1) | ||||
|                dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]); | ||||
|             break; | ||||
|          case ((4)*8+(2)): | ||||
|             for(i=x-1; i >= 0; --i, src += 4, dest += 2) | ||||
|                dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = src[3]; | ||||
|             break; | ||||
|          case ((4)*8+(3)): | ||||
|             for(i=x-1; i >= 0; --i, src += 4, dest += 3) | ||||
|                dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; | ||||
|             break; | ||||
|          default: | ||||
|             break; | ||||
|       } | ||||
| 
 | ||||
|    } | ||||
| 
 | ||||
|    return good; | ||||
| } | ||||
| 
 | ||||
| /* Microsoft/Windows BMP image */ | ||||
| 
 | ||||
| /* returns 0..31 for the highest set bit */ | ||||
| static int rbmp__high_bit(unsigned int z) | ||||
| { | ||||
|    int n=0; | ||||
|    if (z == 0) | ||||
|       return -1; | ||||
|    if (z >= 0x10000) n += 16, z >>= 16; | ||||
|    if (z >= 0x00100) n +=  8, z >>=  8; | ||||
|    if (z >= 0x00010) n +=  4, z >>=  4; | ||||
|    if (z >= 0x00004) n +=  2, z >>=  2; | ||||
|    if (z >= 0x00002) n +=  1, z >>=  1; | ||||
|    return n; | ||||
| } | ||||
| 
 | ||||
| static int rbmp__bitcount(unsigned int a) | ||||
| { | ||||
|    a = (a & 0x55555555) + ((a >>  1) & 0x55555555); /* max 2 */ | ||||
|    a = (a & 0x33333333) + ((a >>  2) & 0x33333333); /* max 4 */ | ||||
|    a = (a + (a >> 4)) & 0x0f0f0f0f; /* max 8 per 4, now 8 bits */ | ||||
|    a = (a + (a >> 8));              /* max 16 per 8 bits */ | ||||
|    a = (a + (a >> 16));             /* max 32 per 8 bits */ | ||||
|    return a & 0xff; | ||||
| } | ||||
| 
 | ||||
| static int rbmp__shiftsigned(int v, int shift, int bits) | ||||
| { | ||||
|    int result; | ||||
|    int z=0; | ||||
| 
 | ||||
|    if (shift < 0) | ||||
|       v <<= -shift; | ||||
|    else | ||||
|       v >>= shift; | ||||
| 
 | ||||
|    result = v; | ||||
|    z      = bits; | ||||
| 
 | ||||
|    while (z < 8) | ||||
|    { | ||||
|       result += v >> z; | ||||
|       z += bits; | ||||
|    } | ||||
|    return result; | ||||
| } | ||||
| 
 | ||||
| static unsigned char *rbmp__bmp_load(rbmp__context *s, unsigned *x, unsigned *y, | ||||
|       int *comp, int req_comp) | ||||
| { | ||||
|    unsigned char *out; | ||||
|    int bpp, flip_vertically, pad, target, offset, hsz; | ||||
|    int psize=0,i,j,width; | ||||
|    unsigned int mr=0,mg=0,mb=0,ma=0; | ||||
| 
 | ||||
|    /* Corrupt BMP? */ | ||||
|    if (rbmp__get8(s) != 'B' || rbmp__get8(s) != 'M') | ||||
|       return 0; | ||||
| 
 | ||||
|    rbmp__get32le(s); /* discard filesize */ | ||||
|    rbmp__get16le(s); /* discard reserved */ | ||||
|    rbmp__get16le(s); /* discard reserved */ | ||||
|    offset = rbmp__get32le(s); | ||||
|    hsz = rbmp__get32le(s); | ||||
| 
 | ||||
|    /* BMP type not supported? */ | ||||
|    if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (hsz == 12) | ||||
|    { | ||||
|       s->img_x = rbmp__get16le(s); | ||||
|       s->img_y = rbmp__get16le(s); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       s->img_x = rbmp__get32le(s); | ||||
|       s->img_y = rbmp__get32le(s); | ||||
|    } | ||||
| 
 | ||||
|    /* Bad BMP? */ | ||||
|    if (rbmp__get16le(s) != 1) | ||||
|       return 0; | ||||
| 
 | ||||
|    bpp = rbmp__get16le(s); | ||||
| 
 | ||||
|    /* BMP 1-bit type not supported? */ | ||||
|    if (bpp == 1) | ||||
|       return 0; | ||||
| 
 | ||||
|    flip_vertically = ((int) s->img_y) > 0; | ||||
|    s->img_y        = abs((int) s->img_y); | ||||
| 
 | ||||
|    if (hsz == 12) | ||||
|    { | ||||
|       if (bpp < 24) | ||||
|          psize = (offset - 14 - 24) / 3; | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       int compress = rbmp__get32le(s); | ||||
| 
 | ||||
|       /* BMP RLE type not supported? */ | ||||
|       if (compress == 1 || compress == 2) | ||||
|          return 0; | ||||
| 
 | ||||
|       rbmp__get32le(s); /* discard sizeof */ | ||||
|       rbmp__get32le(s); /* discard hres */ | ||||
|       rbmp__get32le(s); /* discard vres */ | ||||
|       rbmp__get32le(s); /* discard colors used */ | ||||
|       rbmp__get32le(s); /* discard max important */ | ||||
| 
 | ||||
|       if (hsz == 40 || hsz == 56) | ||||
|       { | ||||
|          if (hsz == 56) | ||||
|          { | ||||
|             rbmp__get32le(s); | ||||
|             rbmp__get32le(s); | ||||
|             rbmp__get32le(s); | ||||
|             rbmp__get32le(s); | ||||
|          } | ||||
|          if (bpp == 16 || bpp == 32) | ||||
|          { | ||||
|             switch (compress) | ||||
|             { | ||||
|                case 0: | ||||
| #if 0 | ||||
|                   if (bpp == 32) | ||||
|                   { | ||||
|                      mr = 0xffu << 16; | ||||
|                      mg = 0xffu <<  8; | ||||
|                      mb = 0xffu <<  0; | ||||
|                      ma = 0xffu << 24; | ||||
|                   } | ||||
|                   else | ||||
|                   { | ||||
|                      mr = 31u << 10; | ||||
|                      mg = 31u <<  5; | ||||
|                      mb = 31u <<  0; | ||||
|                   } | ||||
| #endif | ||||
|                   break; | ||||
|                case 3: | ||||
|                   mr = rbmp__get32le(s); | ||||
|                   mg = rbmp__get32le(s); | ||||
|                   mb = rbmp__get32le(s); | ||||
|                   /* not documented, but generated by
 | ||||
|                    * Photoshop and handled by MS Paint */ | ||||
|                   /* Bad BMP ?*/ | ||||
|                   if (mr == mg && mg == mb) | ||||
|                      return 0; | ||||
|                   break; | ||||
|                default: | ||||
| #if 0 | ||||
|                   mr = mg = mb = 0; | ||||
| #endif | ||||
|                   break; | ||||
|             } | ||||
| 
 | ||||
|             /* Bad BMP? */ | ||||
|             return 0; | ||||
|          } | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|          mr = rbmp__get32le(s); | ||||
|          mg = rbmp__get32le(s); | ||||
|          mb = rbmp__get32le(s); | ||||
|          ma = rbmp__get32le(s); | ||||
|          rbmp__get32le(s); /* Discard color space */ | ||||
|          for (i=0; i < 12; ++i) | ||||
|             rbmp__get32le(s); /* Discard color space parameters */ | ||||
|          if (hsz == 124) | ||||
|          { | ||||
|             rbmp__get32le(s); /* Discard rendering intent */ | ||||
|             rbmp__get32le(s); /* Discard offset of profile data */ | ||||
|             rbmp__get32le(s); /* Discard size of profile data */ | ||||
|             rbmp__get32le(s); /* Discard reserved */ | ||||
|          } | ||||
|       } | ||||
|       if (bpp < 16) | ||||
|          psize = (offset - 14 - hsz) >> 2; | ||||
|    } | ||||
|    s->img_n = ma ? 4 : 3; | ||||
|    if (req_comp && req_comp >= 3) /* We can directly decode 3 or 4 */ | ||||
|       target = req_comp; | ||||
|    else | ||||
|       target = s->img_n; /* If they want monochrome, we'll post-convert */ | ||||
| 
 | ||||
|    out = (unsigned char *) malloc(target * s->img_x * s->img_y); | ||||
| 
 | ||||
|    if (!out) | ||||
|       return 0; | ||||
| 
 | ||||
|    if (bpp < 16) | ||||
|    { | ||||
|       unsigned char pal[256][4]; | ||||
|       int z=0; | ||||
| 
 | ||||
|       /* Corrupt BMP? */ | ||||
|       if (psize == 0 || psize > 256) | ||||
|       { | ||||
|          free(out); | ||||
|          return 0; | ||||
|       } | ||||
| 
 | ||||
|       for (i=0; i < psize; ++i) | ||||
|       { | ||||
|          pal[i][2] = rbmp__get8(s); | ||||
|          pal[i][1] = rbmp__get8(s); | ||||
|          pal[i][0] = rbmp__get8(s); | ||||
|          if (hsz != 12) rbmp__get8(s); | ||||
|          pal[i][3] = 255; | ||||
|       } | ||||
| 
 | ||||
|       rbmp__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); | ||||
|       if (bpp == 4) | ||||
|          width = (s->img_x + 1) >> 1; | ||||
|       else if (bpp == 8) | ||||
|          width = s->img_x; | ||||
|       else | ||||
|       { | ||||
|          /* Corrupt BMP */ | ||||
|          free(out); | ||||
|          return 0; | ||||
|       } | ||||
| 
 | ||||
|       pad = (-width)&3; | ||||
|       for (j=0; j < (int) s->img_y; ++j) | ||||
|       { | ||||
|          for (i=0; i < (int) s->img_x; i += 2) | ||||
|          { | ||||
|             int v=rbmp__get8(s),v2=0; | ||||
|             if (bpp == 4) | ||||
|             { | ||||
|                v2 = v & 15; | ||||
|                v >>= 4; | ||||
|             } | ||||
|             out[z++] = pal[v][0]; | ||||
|             out[z++] = pal[v][1]; | ||||
|             out[z++] = pal[v][2]; | ||||
|             if (target == 4) | ||||
|                out[z++] = 255; | ||||
| 
 | ||||
|             if (i+1 == (int)s->img_x) | ||||
|                break; | ||||
| 
 | ||||
|             v = (bpp == 8) ? rbmp__get8(s) : v2; | ||||
|             out[z++] = pal[v][0]; | ||||
|             out[z++] = pal[v][1]; | ||||
|             out[z++] = pal[v][2]; | ||||
| 
 | ||||
|             if (target == 4) | ||||
|                out[z++] = 255; | ||||
|          } | ||||
|          rbmp__skip(s, pad); | ||||
|       } | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       int rshift=0; | ||||
|       int gshift=0; | ||||
|       int bshift=0; | ||||
|       int ashift=0; | ||||
|       int rcount=0; | ||||
|       int gcount=0; | ||||
|       int bcount=0; | ||||
|       int acount=0; | ||||
|       int z = 0; | ||||
|       int easy=0; | ||||
| 
 | ||||
|       rbmp__skip(s, offset - 14 - hsz); | ||||
| 
 | ||||
|       if (bpp == 24) | ||||
|          width = 3 * s->img_x; | ||||
|       else if (bpp == 16) | ||||
|          width = 2*s->img_x; | ||||
|       else /* bpp = 32 and pad = 0 */ | ||||
|          width=0; | ||||
| 
 | ||||
|       pad = (-width) & 3; | ||||
| 
 | ||||
|       switch (bpp) | ||||
|       { | ||||
|          case 24: | ||||
|             easy = 1; | ||||
|             break; | ||||
|          case 32: | ||||
|             if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) | ||||
|                easy = 2; | ||||
|             break; | ||||
|          default: | ||||
|             break; | ||||
|       } | ||||
| 
 | ||||
|       if (!easy) | ||||
|       { | ||||
|          /* Corrupt BMP? */ | ||||
|          if (!mr || !mg || !mb) | ||||
|          { | ||||
|             free(out); | ||||
|             return 0; | ||||
|          } | ||||
| 
 | ||||
|          /* right shift amt to put high bit in position #7 */ | ||||
|          rshift = rbmp__high_bit(mr)-7; | ||||
|          rcount = rbmp__bitcount(mr); | ||||
|          gshift = rbmp__high_bit(mg)-7; | ||||
|          gcount = rbmp__bitcount(mg); | ||||
|          bshift = rbmp__high_bit(mb)-7; | ||||
|          bcount = rbmp__bitcount(mb); | ||||
|          ashift = rbmp__high_bit(ma)-7; | ||||
|          acount = rbmp__bitcount(ma); | ||||
|       } | ||||
|       for (j=0; j < (int) s->img_y; ++j) | ||||
|       { | ||||
|          if (easy) | ||||
|          { | ||||
|             for (i=0; i < (int) s->img_x; ++i) | ||||
|             { | ||||
|                unsigned char a; | ||||
|                out[z+2] = rbmp__get8(s); | ||||
|                out[z+1] = rbmp__get8(s); | ||||
|                out[z+0] = rbmp__get8(s); | ||||
|                z += 3; | ||||
|                a = (easy == 2 ? rbmp__get8(s) : 255); | ||||
|                if (target == 4) | ||||
|                   out[z++] = a; | ||||
|             } | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             for (i=0; i < (int) s->img_x; ++i) | ||||
|             { | ||||
|                uint32_t v = (bpp == 16 ? (uint32_t) rbmp__get16le(s) : rbmp__get32le(s)); | ||||
|                int a; | ||||
|                out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mr, rshift, rcount)); | ||||
|                out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mg, gshift, gcount)); | ||||
|                out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mb, bshift, bcount)); | ||||
|                a = (ma ? rbmp__shiftsigned(v & ma, ashift, acount) : 255); | ||||
|                if (target == 4) | ||||
|                   out[z++] = RBMP__BYTECAST(a); | ||||
|             } | ||||
|          } | ||||
|          rbmp__skip(s, pad); | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    if (flip_vertically) | ||||
|    { | ||||
|       unsigned char t; | ||||
|       for (j=0; j < (int) s->img_y>>1; ++j) | ||||
|       { | ||||
|          unsigned char *p1 = out +      j     *s->img_x*target; | ||||
|          unsigned char *p2 = out + (s->img_y-1-j)*s->img_x*target; | ||||
|          for (i=0; i < (int) s->img_x*target; ++i) | ||||
|          { | ||||
|             t = p1[i], p1[i] = p2[i], p2[i] = t; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    if ( | ||||
|             req_comp | ||||
|          && (req_comp >= 1 && req_comp <= 4) | ||||
|          && (req_comp != target)) | ||||
|    { | ||||
|       unsigned char *tmp = rbmp__convert_format(out, target, req_comp, s->img_x, s->img_y); | ||||
| 
 | ||||
|       free(out); | ||||
|       out = NULL; | ||||
| 
 | ||||
|       if (!tmp) | ||||
|          return NULL; | ||||
| 
 | ||||
|       out = tmp; | ||||
|    } | ||||
| 
 | ||||
|    *x = s->img_x; | ||||
|    *y = s->img_y; | ||||
| 
 | ||||
|    if (comp) | ||||
|       *comp = s->img_n; | ||||
| 
 | ||||
|    return out; | ||||
| } | ||||
| 
 | ||||
| static unsigned char *rbmp_load_from_memory(unsigned char const *buffer, int len, | ||||
|       unsigned *x, unsigned *y, int *comp, int req_comp) | ||||
| { | ||||
|    rbmp__context s; | ||||
| 
 | ||||
|    s.img_buffer          = (unsigned char*)buffer; | ||||
|    s.img_buffer_original = (unsigned char*)buffer; | ||||
|    s.img_buffer_end      = (unsigned char*)buffer+len; | ||||
| 
 | ||||
|    return rbmp__bmp_load(&s,x,y,comp,req_comp); | ||||
| } | ||||
| 
 | ||||
| static void rbmp_convert_frame(uint32_t *frame, unsigned width, unsigned height) | ||||
| { | ||||
|    uint32_t *end = frame + (width * height * sizeof(uint32_t))/4; | ||||
| 
 | ||||
|    while(frame < end) | ||||
|    { | ||||
|       uint32_t pixel = *frame; | ||||
|       *frame = (pixel & 0xff00ff00) | ((pixel << 16) & 0x00ff0000) | ((pixel >> 16) & 0xff); | ||||
|       frame++; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| int rbmp_process_image(rbmp_t *rbmp, void **buf_data, | ||||
|       size_t size, unsigned *width, unsigned *height) | ||||
| { | ||||
|    int comp; | ||||
| 
 | ||||
|    if (!rbmp) | ||||
|       return IMAGE_PROCESS_ERROR; | ||||
| 
 | ||||
|    rbmp->output_image   = (uint32_t*)rbmp_load_from_memory(rbmp->buff_data, | ||||
|                            (int)size, width, height, &comp, 4); | ||||
|    *buf_data             = rbmp->output_image; | ||||
| 
 | ||||
|    rbmp_convert_frame(rbmp->output_image, *width, *height); | ||||
| 
 | ||||
|    return IMAGE_PROCESS_END; | ||||
| } | ||||
| 
 | ||||
| bool rbmp_set_buf_ptr(rbmp_t *rbmp, void *data) | ||||
| { | ||||
|    if (!rbmp) | ||||
|       return false; | ||||
| 
 | ||||
|    rbmp->buff_data = (uint8_t*)data; | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| void rbmp_free(rbmp_t *rbmp) | ||||
| { | ||||
|    if (!rbmp) | ||||
|       return; | ||||
| 
 | ||||
|    free(rbmp); | ||||
| } | ||||
| 
 | ||||
| rbmp_t *rbmp_alloc(void) | ||||
| { | ||||
|    rbmp_t *rbmp = (rbmp_t*)calloc(1, sizeof(*rbmp)); | ||||
|    if (!rbmp) | ||||
|       return NULL; | ||||
|    return rbmp; | ||||
| } | ||||
|  | @ -0,0 +1,232 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (rbmp_encode.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <streams/file_stream.h> | ||||
| #include <formats/rbmp.h> | ||||
| 
 | ||||
| static bool write_header_bmp(RFILE *file, unsigned width, unsigned height, bool is32bpp) | ||||
| { | ||||
|    uint8_t header[54]; | ||||
|    unsigned line_size  = (width * (is32bpp?4:3) + 3) & ~3; | ||||
|    unsigned size       = line_size * height + 54; | ||||
|    unsigned size_array = line_size * height; | ||||
| 
 | ||||
|    /* Generic BMP stuff. */ | ||||
| 
 | ||||
|    /* signature */ | ||||
|    header[0] = 'B'; | ||||
|    header[1] = 'M'; | ||||
|    /* file size */ | ||||
|    header[2] = (uint8_t)(size >> 0); | ||||
|    header[3] = (uint8_t)(size >> 8); | ||||
|    header[4] = (uint8_t)(size >> 16); | ||||
|    header[5] = (uint8_t)(size >> 24); | ||||
|    /* reserved */ | ||||
|    header[6] = 0; | ||||
|    header[7] = 0; | ||||
|    header[8] = 0; | ||||
|    header[9] = 0; | ||||
|    /* offset */ | ||||
|    header[10] = 54; | ||||
|    header[11] = 0; | ||||
|    header[12] = 0; | ||||
|    header[13] = 0; | ||||
|    /* DIB size */ | ||||
|    header[14] = 40; | ||||
|    header[15] = 0; | ||||
|    header[16] = 0; | ||||
|    header[17] = 0; | ||||
|    /* Width */ | ||||
|    header[18] = (uint8_t)(width >> 0); | ||||
|    header[19] = (uint8_t)(width >> 8); | ||||
|    header[20] = (uint8_t)(width >> 16); | ||||
|    header[21] = (uint8_t)(width >> 24); | ||||
|    /* Height */ | ||||
|    header[22] = (uint8_t)(height >> 0); | ||||
|    header[23] = (uint8_t)(height >> 8); | ||||
|    header[24] = (uint8_t)(height >> 16); | ||||
|    header[25] = (uint8_t)(height >> 24); | ||||
|    /* Color planes */ | ||||
|    header[26] = 1; | ||||
|    header[27] = 0; | ||||
|    /* Bits per pixel */ | ||||
|    header[28] = is32bpp?32:24; | ||||
|    header[29] = 0; | ||||
|    /* Compression method */ | ||||
|    header[30] = 0; | ||||
|    header[31] = 0; | ||||
|    header[32] = 0; | ||||
|    header[33] = 0; | ||||
|    /* Image data size */ | ||||
|    header[34] = (uint8_t)(size_array >> 0); | ||||
|    header[35] = (uint8_t)(size_array >> 8); | ||||
|    header[36] = (uint8_t)(size_array >> 16); | ||||
|    header[37] = (uint8_t)(size_array >> 24); | ||||
|    /* Horizontal resolution */ | ||||
|    header[38] = 19; | ||||
|    header[39] = 11; | ||||
|    header[40] = 0; | ||||
|    header[41] = 0; | ||||
|    /* Vertical resolution */ | ||||
|    header[42] = 19; | ||||
|    header[43] = 11; | ||||
|    header[44] = 0; | ||||
|    header[45] = 0; | ||||
|    /* Palette size */ | ||||
|    header[46] = 0; | ||||
|    header[47] = 0; | ||||
|    header[48] = 0; | ||||
|    header[49] = 0; | ||||
|    /* Important color count */ | ||||
|    header[50] = 0; | ||||
|    header[51] = 0; | ||||
|    header[52] = 0; | ||||
|    header[53] = 0; | ||||
| 
 | ||||
|    return filestream_write(file, header, sizeof(header)) == sizeof(header); | ||||
| } | ||||
| 
 | ||||
| static void dump_line_565_to_24(uint8_t *line, const uint16_t *src, unsigned width) | ||||
| { | ||||
|    unsigned i; | ||||
| 
 | ||||
|    for (i = 0; i < width; i++) | ||||
|    { | ||||
|       uint16_t pixel = *src++; | ||||
|       uint8_t b = (pixel >>  0) & 0x1f; | ||||
|       uint8_t g = (pixel >>  5) & 0x3f; | ||||
|       uint8_t r = (pixel >> 11) & 0x1f; | ||||
|       *line++   = (b << 3) | (b >> 2); | ||||
|       *line++   = (g << 2) | (g >> 4); | ||||
|       *line++   = (r << 3) | (r >> 2); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void dump_line_32_to_24(uint8_t *line, const uint32_t *src, unsigned width) | ||||
| { | ||||
|    unsigned i; | ||||
| 
 | ||||
|    for (i = 0; i < width; i++) | ||||
|    { | ||||
|       uint32_t pixel = *src++; | ||||
|       *line++ = (pixel >>  0) & 0xff; | ||||
|       *line++ = (pixel >>  8) & 0xff; | ||||
|       *line++ = (pixel >> 16) & 0xff; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void dump_content(RFILE *file, const void *frame, | ||||
|       int width, int height, int pitch, enum rbmp_source_type type) | ||||
| { | ||||
|    int j; | ||||
|    size_t line_size; | ||||
|    uint8_t *line       = NULL; | ||||
|    int bytes_per_pixel = (type==RBMP_SOURCE_TYPE_ARGB8888?4:3); | ||||
|    union | ||||
|    { | ||||
|       const uint8_t *u8; | ||||
|       const uint16_t *u16; | ||||
|       const uint32_t *u32; | ||||
|    } u; | ||||
| 
 | ||||
|    u.u8      = (const uint8_t*)frame; | ||||
|    line_size = (width * bytes_per_pixel + 3) & ~3; | ||||
| 
 | ||||
|    switch (type) | ||||
|    { | ||||
|       case RBMP_SOURCE_TYPE_BGR24: | ||||
|          { | ||||
|             /* BGR24 byte order input matches output. Can directly copy, but... need to make sure we pad it. */ | ||||
|             uint32_t zeros = 0; | ||||
|             int pad        = (int)(line_size-pitch); | ||||
|             for (j = 0; j < height; j++, u.u8 += pitch) | ||||
|             { | ||||
|                filestream_write(file, u.u8, pitch); | ||||
|                if(pad != 0) | ||||
|                   filestream_write(file, &zeros, pad); | ||||
|             } | ||||
|          } | ||||
|          break; | ||||
|       case RBMP_SOURCE_TYPE_ARGB8888: | ||||
|          /* ARGB8888 byte order input matches output. Can directly copy. */ | ||||
|          for (j = 0; j < height; j++, u.u8 += pitch) | ||||
|             filestream_write(file, u.u8, line_size); | ||||
|          return; | ||||
|       default: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    /* allocate line buffer, and initialize the final four bytes to zero, for deterministic padding */ | ||||
|    line = (uint8_t*)malloc(line_size); | ||||
|    if (!line) | ||||
|       return; | ||||
|    *(uint32_t*)(line + line_size - 4) = 0; | ||||
| 
 | ||||
|    switch (type) | ||||
|    { | ||||
|       case RBMP_SOURCE_TYPE_XRGB888: | ||||
|          for (j = 0; j < height; j++, u.u8 += pitch) | ||||
|          { | ||||
|             dump_line_32_to_24(line, u.u32, width); | ||||
|             filestream_write(file, line, line_size); | ||||
|          } | ||||
|          break; | ||||
|       case RBMP_SOURCE_TYPE_RGB565: | ||||
|          for (j = 0; j < height; j++, u.u8 += pitch) | ||||
|          { | ||||
|             dump_line_565_to_24(line, u.u16, width); | ||||
|             filestream_write(file, line, line_size); | ||||
|          } | ||||
|          break; | ||||
|       default: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    /* Free allocated line buffer */ | ||||
|    free(line); | ||||
| } | ||||
| 
 | ||||
| bool rbmp_save_image( | ||||
|       const char *filename, | ||||
|       const void *frame, | ||||
|       unsigned width, unsigned height, | ||||
|       unsigned pitch, enum rbmp_source_type type) | ||||
| { | ||||
|    bool ret    = false; | ||||
|    RFILE *file = filestream_open(filename, | ||||
|          RETRO_VFS_FILE_ACCESS_WRITE, | ||||
|          RETRO_VFS_FILE_ACCESS_HINT_NONE); | ||||
|    if (!file) | ||||
|       return false; | ||||
| 
 | ||||
|    ret = write_header_bmp(file, width, height, type==RBMP_SOURCE_TYPE_ARGB8888); | ||||
| 
 | ||||
|    if (ret) | ||||
|       dump_content(file, frame, width, height, pitch, type); | ||||
| 
 | ||||
|    filestream_close(file); | ||||
| 
 | ||||
|    return ret; | ||||
| } | ||||
|  | @ -0,0 +1,315 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (image_texture.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stddef.h> | ||||
| 
 | ||||
| #include <boolean.h> | ||||
| #include <formats/image.h> | ||||
| #include <file/nbio.h> | ||||
| 
 | ||||
| enum video_image_format | ||||
| { | ||||
|    IMAGE_FORMAT_NONE = 0, | ||||
|    IMAGE_FORMAT_TGA, | ||||
|    IMAGE_FORMAT_PNG, | ||||
|    IMAGE_FORMAT_JPEG, | ||||
|    IMAGE_FORMAT_BMP | ||||
| }; | ||||
| 
 | ||||
| bool image_texture_set_color_shifts( | ||||
|       unsigned *r_shift, unsigned *g_shift, unsigned *b_shift, | ||||
|       unsigned *a_shift, | ||||
|       struct texture_image *out_img | ||||
|       ) | ||||
| { | ||||
|    *a_shift             = 24; | ||||
|    *r_shift             = 16; | ||||
|    *g_shift             = 8; | ||||
|    *b_shift             = 0; | ||||
| 
 | ||||
|    if (out_img->supports_rgba) | ||||
|    { | ||||
|       *r_shift = 0; | ||||
|       *b_shift = 16; | ||||
|       return true; | ||||
|    } | ||||
| 
 | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| bool image_texture_color_convert(unsigned r_shift, | ||||
|       unsigned g_shift, unsigned b_shift, unsigned a_shift, | ||||
|       struct texture_image *out_img) | ||||
| { | ||||
|    /* This is quite uncommon. */ | ||||
|    if (a_shift != 24 || r_shift != 16 || g_shift != 8 || b_shift != 0) | ||||
|    { | ||||
|       uint32_t i; | ||||
|       uint32_t num_pixels = out_img->width * out_img->height; | ||||
|       uint32_t *pixels    = (uint32_t*)out_img->pixels; | ||||
| 
 | ||||
|       for (i = 0; i < num_pixels; i++) | ||||
|       { | ||||
|          uint32_t col = pixels[i]; | ||||
|          uint8_t a    = (uint8_t)(col >> 24); | ||||
|          uint8_t r    = (uint8_t)(col >> 16); | ||||
|          uint8_t g    = (uint8_t)(col >>  8); | ||||
|          uint8_t b    = (uint8_t)(col >>  0); | ||||
|          pixels[i]    = (a << a_shift) | | ||||
|             (r << r_shift) | (g << g_shift) | (b << b_shift); | ||||
|       } | ||||
| 
 | ||||
|       return true; | ||||
|    } | ||||
| 
 | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| #ifdef GEKKO | ||||
| 
 | ||||
| #define GX_BLIT_LINE_32(off) \ | ||||
| { \ | ||||
|    unsigned x; \ | ||||
|    const uint16_t *tmp_src = src; \ | ||||
|    uint16_t       *tmp_dst = dst; \ | ||||
|    for (x = 0; x < width2 >> 3; x++, tmp_src += 8, tmp_dst += 32) \ | ||||
|    { \ | ||||
|       tmp_dst[  0 + off] = tmp_src[0]; \ | ||||
|       tmp_dst[ 16 + off] = tmp_src[1]; \ | ||||
|       tmp_dst[  1 + off] = tmp_src[2]; \ | ||||
|       tmp_dst[ 17 + off] = tmp_src[3]; \ | ||||
|       tmp_dst[  2 + off] = tmp_src[4]; \ | ||||
|       tmp_dst[ 18 + off] = tmp_src[5]; \ | ||||
|       tmp_dst[  3 + off] = tmp_src[6]; \ | ||||
|       tmp_dst[ 19 + off] = tmp_src[7]; \ | ||||
|    } \ | ||||
|    src += tmp_pitch; \ | ||||
| } | ||||
| 
 | ||||
| static bool image_texture_internal_gx_convert_texture32( | ||||
|       struct texture_image *image) | ||||
| { | ||||
|    unsigned tmp_pitch, width2, i; | ||||
|    const uint16_t *src = NULL; | ||||
|    uint16_t *dst       = NULL; | ||||
|    /* Memory allocation in libogc is extremely primitive so try
 | ||||
|     * to avoid gaps in memory when converting by copying over to | ||||
|     * a temporary buffer first, then converting over into | ||||
|     * main buffer again. */ | ||||
|    void *tmp           = malloc(image->width | ||||
|          * image->height * sizeof(uint32_t)); | ||||
| 
 | ||||
|    if (!tmp) | ||||
|       return false; | ||||
| 
 | ||||
|    memcpy(tmp, image->pixels, image->width | ||||
|          * image->height * sizeof(uint32_t)); | ||||
|    tmp_pitch = (image->width * sizeof(uint32_t)) >> 1; | ||||
| 
 | ||||
|    image->width       &= ~3; | ||||
|    image->height      &= ~3; | ||||
|    width2              = image->width << 1; | ||||
|    src                 = (uint16_t*)tmp; | ||||
|    dst                 = (uint16_t*)image->pixels; | ||||
| 
 | ||||
|    for (i = 0; i < image->height; i += 4, dst += 4 * width2) | ||||
|    { | ||||
|       GX_BLIT_LINE_32(0) | ||||
|       GX_BLIT_LINE_32(4) | ||||
|       GX_BLIT_LINE_32(8) | ||||
|       GX_BLIT_LINE_32(12) | ||||
|    } | ||||
| 
 | ||||
|    free(tmp); | ||||
|    return true; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static bool image_texture_load_internal( | ||||
|       enum image_type_enum type, | ||||
|       void *ptr, | ||||
|       size_t len, | ||||
|       struct texture_image *out_img, | ||||
|       unsigned a_shift, unsigned r_shift, | ||||
|       unsigned g_shift, unsigned b_shift) | ||||
| { | ||||
|    int ret; | ||||
|    bool success = false; | ||||
|    void *img    = image_transfer_new(type); | ||||
| 
 | ||||
|    if (!img) | ||||
|       goto end; | ||||
| 
 | ||||
|    image_transfer_set_buffer_ptr(img, type, (uint8_t*)ptr); | ||||
| 
 | ||||
|    if (!image_transfer_start(img, type)) | ||||
|       goto end; | ||||
| 
 | ||||
|    while (image_transfer_iterate(img, type)); | ||||
| 
 | ||||
|    if (!image_transfer_is_valid(img, type)) | ||||
|       goto end; | ||||
| 
 | ||||
|    do | ||||
|    { | ||||
|       ret = image_transfer_process(img, type, | ||||
|             (uint32_t**)&out_img->pixels, len, &out_img->width, | ||||
|             &out_img->height); | ||||
|    }while(ret == IMAGE_PROCESS_NEXT); | ||||
| 
 | ||||
|    if (ret == IMAGE_PROCESS_ERROR || ret == IMAGE_PROCESS_ERROR_END) | ||||
|       goto end; | ||||
| 
 | ||||
|    image_texture_color_convert(r_shift, g_shift, b_shift, | ||||
|          a_shift, out_img); | ||||
| 
 | ||||
| #ifdef GEKKO | ||||
|    if (!image_texture_internal_gx_convert_texture32(out_img)) | ||||
|    { | ||||
|       image_texture_free(out_img); | ||||
|       goto end; | ||||
|    } | ||||
| #endif | ||||
| 
 | ||||
|    success = true; | ||||
| 
 | ||||
| end: | ||||
|    if (img) | ||||
|       image_transfer_free(img, type); | ||||
| 
 | ||||
|    return success; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void image_texture_free(struct texture_image *img) | ||||
| { | ||||
|    if (!img) | ||||
|       return; | ||||
| 
 | ||||
|    if (img->pixels) | ||||
|       free(img->pixels); | ||||
|    img->width  = 0; | ||||
|    img->height = 0; | ||||
|    img->pixels = NULL; | ||||
| } | ||||
| 
 | ||||
| static enum video_image_format image_texture_get_type(const char *path) | ||||
| { | ||||
| #ifdef HAVE_RTGA | ||||
|    if (strstr(path, ".tga")) | ||||
|       return IMAGE_FORMAT_TGA; | ||||
| #endif | ||||
| #ifdef HAVE_RPNG | ||||
|    if (strstr(path, ".png")) | ||||
|       return IMAGE_FORMAT_PNG; | ||||
| #endif | ||||
| #ifdef HAVE_RJPEG | ||||
|    if (strstr(path, ".jpg") || strstr(path, ".jpeg")) | ||||
|       return IMAGE_FORMAT_JPEG; | ||||
| #endif | ||||
| #ifdef HAVE_RBMP | ||||
|    if (strstr(path, ".bmp")) | ||||
|       return IMAGE_FORMAT_BMP; | ||||
| #endif | ||||
|    return IMAGE_FORMAT_NONE; | ||||
| } | ||||
| 
 | ||||
| static enum image_type_enum image_texture_convert_fmt_to_type(enum video_image_format fmt) | ||||
| { | ||||
|    switch (fmt) | ||||
|    { | ||||
| #ifdef HAVE_RPNG | ||||
|       case IMAGE_FORMAT_PNG: | ||||
|          return IMAGE_TYPE_PNG; | ||||
| #endif | ||||
| #ifdef HAVE_RJPEG | ||||
|       case IMAGE_FORMAT_JPEG: | ||||
|          return IMAGE_TYPE_JPEG; | ||||
| #endif | ||||
| #ifdef HAVE_RBMP | ||||
|       case IMAGE_FORMAT_BMP: | ||||
|          return IMAGE_TYPE_BMP; | ||||
| #endif | ||||
| #ifdef HAVE_RTGA | ||||
|       case IMAGE_FORMAT_TGA: | ||||
|          return IMAGE_TYPE_TGA; | ||||
| #endif | ||||
|       case IMAGE_FORMAT_NONE: | ||||
|       default: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return IMAGE_TYPE_NONE; | ||||
| } | ||||
| 
 | ||||
| bool image_texture_load(struct texture_image *out_img, | ||||
|       const char *path) | ||||
| { | ||||
|    unsigned r_shift, g_shift, b_shift, a_shift; | ||||
|    size_t file_len             = 0; | ||||
|    struct nbio_t      *handle  = NULL; | ||||
|    void                   *ptr = NULL; | ||||
|    enum video_image_format fmt = image_texture_get_type(path); | ||||
| 
 | ||||
|    image_texture_set_color_shifts(&r_shift, &g_shift, &b_shift, | ||||
|          &a_shift, out_img); | ||||
| 
 | ||||
|    if (fmt != IMAGE_FORMAT_NONE) | ||||
|    { | ||||
|       handle = (struct nbio_t*)nbio_open(path, NBIO_READ); | ||||
|       if (!handle) | ||||
|          goto error; | ||||
|       nbio_begin_read(handle); | ||||
| 
 | ||||
|       while (!nbio_iterate(handle)); | ||||
| 
 | ||||
|       ptr = nbio_get_ptr(handle, &file_len); | ||||
| 
 | ||||
|       if (!ptr) | ||||
|          goto error; | ||||
| 
 | ||||
|       if (image_texture_load_internal( | ||||
|                image_texture_convert_fmt_to_type(fmt), | ||||
|                ptr, file_len, out_img, | ||||
|                a_shift, r_shift, g_shift, b_shift)) | ||||
|          goto success; | ||||
|    } | ||||
| 
 | ||||
| error: | ||||
|    out_img->supports_rgba = false; | ||||
|    out_img->pixels        = NULL; | ||||
|    out_img->width         = 0; | ||||
|    out_img->height        = 0; | ||||
|    if (handle) | ||||
|       nbio_free(handle); | ||||
| 
 | ||||
|    return false; | ||||
| 
 | ||||
| success: | ||||
|    if (handle) | ||||
|       nbio_free(handle); | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
|  | @ -0,0 +1,286 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (image_transfer.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #ifdef HAVE_RPNG | ||||
| #include <formats/rpng.h> | ||||
| #endif | ||||
| #ifdef HAVE_RJPEG | ||||
| #include <formats/rjpeg.h> | ||||
| #endif | ||||
| #ifdef HAVE_RTGA | ||||
| #include <formats/rtga.h> | ||||
| #endif | ||||
| #ifdef HAVE_RBMP | ||||
| #include <formats/rbmp.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <formats/image.h> | ||||
| 
 | ||||
| void image_transfer_free(void *data, enum image_type_enum type) | ||||
| { | ||||
|    switch (type) | ||||
|    { | ||||
|       case IMAGE_TYPE_TGA: | ||||
| #ifdef HAVE_RTGA | ||||
|          rtga_free((rtga_t*)data); | ||||
| #endif | ||||
|          break; | ||||
|       case IMAGE_TYPE_PNG: | ||||
|          { | ||||
| #ifdef HAVE_RPNG | ||||
|             rpng_t *rpng = (rpng_t*)data; | ||||
|             if (rpng) | ||||
|                rpng_free(rpng); | ||||
| #endif | ||||
|          } | ||||
|          break; | ||||
|       case IMAGE_TYPE_JPEG: | ||||
| #ifdef HAVE_RJPEG | ||||
|          rjpeg_free((rjpeg_t*)data); | ||||
| #endif | ||||
|          break; | ||||
|       case IMAGE_TYPE_BMP: | ||||
| #ifdef HAVE_RBMP | ||||
|          rbmp_free((rbmp_t*)data); | ||||
| #endif | ||||
|          break; | ||||
|       case IMAGE_TYPE_NONE: | ||||
|          break; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void *image_transfer_new(enum image_type_enum type) | ||||
| { | ||||
|    switch (type) | ||||
|    { | ||||
|       case IMAGE_TYPE_PNG: | ||||
| #ifdef HAVE_RPNG | ||||
|          return rpng_alloc(); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_JPEG: | ||||
| #ifdef HAVE_RJPEG | ||||
|          return rjpeg_alloc(); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_TGA: | ||||
| #ifdef HAVE_RTGA | ||||
|          return rtga_alloc(); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_BMP: | ||||
| #ifdef HAVE_RBMP | ||||
|          return rbmp_alloc(); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       default: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| bool image_transfer_start(void *data, enum image_type_enum type) | ||||
| { | ||||
| 
 | ||||
|    switch (type) | ||||
|    { | ||||
|       case IMAGE_TYPE_PNG: | ||||
| #ifdef HAVE_RPNG | ||||
|          if (!rpng_start((rpng_t*)data)) | ||||
|             break; | ||||
|          return true; | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_JPEG: | ||||
| #ifdef HAVE_RJPEG | ||||
|          return true; | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_TGA: | ||||
| #ifdef HAVE_RTGA | ||||
|          return true; | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_BMP: | ||||
|          return true; | ||||
|       case IMAGE_TYPE_NONE: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| bool image_transfer_is_valid( | ||||
|       void *data, | ||||
|       enum image_type_enum type) | ||||
| { | ||||
|    switch (type) | ||||
|    { | ||||
|       case IMAGE_TYPE_PNG: | ||||
| #ifdef HAVE_RPNG | ||||
|          return rpng_is_valid((rpng_t*)data); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_JPEG: | ||||
| #ifdef HAVE_RJPEG | ||||
|          return true; | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_TGA: | ||||
| #ifdef HAVE_RTGA | ||||
|          return true; | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_BMP: | ||||
|          return true; | ||||
|       case IMAGE_TYPE_NONE: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| void image_transfer_set_buffer_ptr( | ||||
|       void *data, | ||||
|       enum image_type_enum type, | ||||
|       void *ptr) | ||||
| { | ||||
|    switch (type) | ||||
|    { | ||||
|       case IMAGE_TYPE_PNG: | ||||
| #ifdef HAVE_RPNG | ||||
|          rpng_set_buf_ptr((rpng_t*)data, (uint8_t*)ptr); | ||||
| #endif | ||||
|          break; | ||||
|       case IMAGE_TYPE_JPEG: | ||||
| #ifdef HAVE_RJPEG | ||||
|          rjpeg_set_buf_ptr((rjpeg_t*)data, (uint8_t*)ptr); | ||||
| #endif | ||||
|          break; | ||||
|       case IMAGE_TYPE_TGA: | ||||
| #ifdef HAVE_RTGA | ||||
|          rtga_set_buf_ptr((rtga_t*)data, (uint8_t*)ptr); | ||||
| #endif | ||||
|          break; | ||||
|       case IMAGE_TYPE_BMP: | ||||
| #ifdef HAVE_RBMP | ||||
|          rbmp_set_buf_ptr((rbmp_t*)data, (uint8_t*)ptr); | ||||
| #endif | ||||
|          break; | ||||
|       case IMAGE_TYPE_NONE: | ||||
|          break; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| int image_transfer_process( | ||||
|       void *data, | ||||
|       enum image_type_enum type, | ||||
|       uint32_t **buf, size_t len, | ||||
|       unsigned *width, unsigned *height) | ||||
| { | ||||
|    switch (type) | ||||
|    { | ||||
|       case IMAGE_TYPE_PNG: | ||||
| #ifdef HAVE_RPNG | ||||
|          if (!rpng_is_valid((rpng_t*)data)) | ||||
|             return IMAGE_PROCESS_ERROR; | ||||
| 
 | ||||
|          return rpng_process_image( | ||||
|                (rpng_t*)data, | ||||
|                (void**)buf, len, width, height); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_JPEG: | ||||
| #ifdef HAVE_RJPEG | ||||
|          return rjpeg_process_image((rjpeg_t*)data, | ||||
|                (void**)buf, len, width, height); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_TGA: | ||||
| #ifdef HAVE_RTGA | ||||
|          return rtga_process_image((rtga_t*)data, | ||||
|                (void**)buf, len, width, height); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_BMP: | ||||
| #ifdef HAVE_RBMP | ||||
|          return rbmp_process_image((rbmp_t*)data, | ||||
|                (void**)buf, len, width, height); | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_NONE: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| bool image_transfer_iterate(void *data, enum image_type_enum type) | ||||
| { | ||||
| 
 | ||||
|    switch (type) | ||||
|    { | ||||
|       case IMAGE_TYPE_PNG: | ||||
| #ifdef HAVE_RPNG | ||||
|          if (!rpng_iterate_image((rpng_t*)data)) | ||||
|             return false; | ||||
| #endif | ||||
|          break; | ||||
|       case IMAGE_TYPE_JPEG: | ||||
| #ifdef HAVE_RJPEG | ||||
|          return false; | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_TGA: | ||||
| #ifdef HAVE_RTGA | ||||
|          return false; | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case IMAGE_TYPE_BMP: | ||||
|          return false; | ||||
|       case IMAGE_TYPE_NONE: | ||||
|          return false; | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,316 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (jsonsax.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <setjmp.h> | ||||
| #include <string.h> | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| #include <formats/jsonsax.h> | ||||
| 
 | ||||
| #ifdef JSONSAX_ERRORS | ||||
| const char* jsonsax_errors[] = | ||||
| { | ||||
|   "Ok", | ||||
|   "Interrupted", | ||||
|   "Missing key", | ||||
|   "Unterminated key", | ||||
|   "Missing value", | ||||
|   "Unterminated object", | ||||
|   "Unterminated array", | ||||
|   "Unterminated string", | ||||
|   "Invalid value" | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| typedef struct | ||||
| { | ||||
|   const jsonsax_handlers_t* handlers; | ||||
| 
 | ||||
|   const char* json; | ||||
|   void*       ud; | ||||
|   jmp_buf     env; | ||||
| } | ||||
| state_t; | ||||
| 
 | ||||
| static INLINE void skip_spaces( state_t* state ) | ||||
| { | ||||
|   while ( isspace( (unsigned char)*state->json ) ) | ||||
|     state->json++; | ||||
| } | ||||
| 
 | ||||
| static INLINE void skip_digits( state_t* state ) | ||||
| { | ||||
|   while ( isdigit( (unsigned char)*state->json ) ) | ||||
|     state->json++; | ||||
| } | ||||
| 
 | ||||
| #define HANDLE_0( event ) \ | ||||
|   do { \ | ||||
|     if ( state->handlers->event && state->handlers->event( state->ud ) ) \ | ||||
|       longjmp( state->env, JSONSAX_INTERRUPTED ); \ | ||||
|   } while ( 0 ) | ||||
| 
 | ||||
| #define HANDLE_1( event, arg1 ) \ | ||||
|   do { \ | ||||
|     if ( state->handlers->event && state->handlers->event( state->ud, arg1 ) ) \ | ||||
|       longjmp( state->env, JSONSAX_INTERRUPTED ); \ | ||||
|   } while ( 0 ) | ||||
| 
 | ||||
| #define HANDLE_2( event, arg1, arg2 ) \ | ||||
|   do { \ | ||||
|     if ( state->handlers->event && state->handlers->event( state->ud, arg1, arg2 ) ) \ | ||||
|       longjmp( state->env, JSONSAX_INTERRUPTED ); \ | ||||
|   } while ( 0 ) | ||||
| 
 | ||||
| static void jsonx_parse_value(state_t* state); | ||||
| 
 | ||||
| static void jsonx_parse_object( state_t* state ) | ||||
| { | ||||
|    state->json++; /* we're sure the current character is a '{' */ | ||||
|    skip_spaces( state ); | ||||
|    HANDLE_0( start_object ); | ||||
| 
 | ||||
|    while ( *state->json != '}' ) | ||||
|    { | ||||
|       const char *name = NULL; | ||||
|       if ( *state->json != '"' ) | ||||
|          longjmp( state->env, JSONSAX_MISSING_KEY ); | ||||
| 
 | ||||
|       name = ++state->json; | ||||
| 
 | ||||
|       for ( ;; ) | ||||
|       { | ||||
|          const char* quote = strchr( state->json, '"' ); | ||||
| 
 | ||||
|          if ( !quote ) | ||||
|             longjmp( state->env, JSONSAX_UNTERMINATED_KEY ); | ||||
| 
 | ||||
|          state->json = quote + 1; | ||||
| 
 | ||||
|          if ( quote[ -1 ] != '\\' ) | ||||
|             break; | ||||
|       } | ||||
| 
 | ||||
|       HANDLE_2( key, name, state->json - name - 1 ); | ||||
|       skip_spaces( state ); | ||||
| 
 | ||||
|       if ( *state->json != ':' ) | ||||
|          longjmp( state->env, JSONSAX_MISSING_VALUE ); | ||||
| 
 | ||||
|       state->json++; | ||||
|       skip_spaces( state ); | ||||
|       jsonx_parse_value( state ); | ||||
|       skip_spaces( state ); | ||||
| 
 | ||||
|       if ( *state->json != ',' ) | ||||
|          break; | ||||
| 
 | ||||
|       state->json++; | ||||
|       skip_spaces( state ); | ||||
|    } | ||||
| 
 | ||||
|    if ( *state->json != '}' ) | ||||
|       longjmp( state->env, JSONSAX_UNTERMINATED_OBJECT ); | ||||
| 
 | ||||
|    state->json++; | ||||
|    HANDLE_0( end_object ); | ||||
| } | ||||
| 
 | ||||
| static void jsonx_parse_array(state_t* state) | ||||
| { | ||||
|    unsigned int ndx = 0; | ||||
| 
 | ||||
|    state->json++; /* we're sure the current character is a '[' */ | ||||
|    skip_spaces( state ); | ||||
|    HANDLE_0( start_array ); | ||||
| 
 | ||||
|    while ( *state->json != ']' ) | ||||
|    { | ||||
|       HANDLE_1( array_index, ndx++ ); | ||||
|       jsonx_parse_value( state ); | ||||
|       skip_spaces( state ); | ||||
| 
 | ||||
|       if ( *state->json != ',' ) | ||||
|          break; | ||||
| 
 | ||||
|       state->json++; | ||||
|       skip_spaces( state ); | ||||
|    } | ||||
| 
 | ||||
|    if ( *state->json != ']' ) | ||||
|       longjmp( state->env, JSONSAX_UNTERMINATED_ARRAY ); | ||||
| 
 | ||||
|    state->json++; | ||||
|    HANDLE_0( end_array ); | ||||
| } | ||||
| 
 | ||||
| static void jsonx_parse_string(state_t* state) | ||||
| { | ||||
|   const char* string = ++state->json; | ||||
| 
 | ||||
|   for ( ;; ) | ||||
|   { | ||||
|     const char* quote = strchr( state->json, '"' ); | ||||
| 
 | ||||
|     if ( !quote ) | ||||
|       longjmp( state->env, JSONSAX_UNTERMINATED_STRING ); | ||||
| 
 | ||||
|     state->json = quote + 1; | ||||
| 
 | ||||
|     if ( quote[ -1 ] != '\\' ) | ||||
|       break; | ||||
|   } | ||||
| 
 | ||||
|   HANDLE_2( string, string, state->json - string - 1 ); | ||||
| } | ||||
| 
 | ||||
| static void jsonx_parse_boolean(state_t* state) | ||||
| { | ||||
|    if ( !strncmp( state->json, "true", 4 ) ) | ||||
|    { | ||||
|       state->json += 4; | ||||
|       HANDLE_1( boolean, 1 ); | ||||
|    } | ||||
|    else if ( !strncmp( state->json, "false", 5 ) ) | ||||
|    { | ||||
|       state->json += 5; | ||||
|       HANDLE_1( boolean, 0 ); | ||||
|    } | ||||
|    else | ||||
|       longjmp( state->env, JSONSAX_INVALID_VALUE ); | ||||
| } | ||||
| 
 | ||||
| static void jsonx_parse_null(state_t* state) | ||||
| { | ||||
|    if ( !strncmp( state->json + 1, "ull", 3 ) ) /* we're sure the current character is a 'n' */ | ||||
|    { | ||||
|       state->json += 4; | ||||
|       HANDLE_0( null ); | ||||
|    } | ||||
|    else | ||||
|       longjmp( state->env, JSONSAX_INVALID_VALUE ); | ||||
| } | ||||
| 
 | ||||
| static void jsonx_parse_number(state_t* state) | ||||
| { | ||||
|    const char* number = state->json; | ||||
| 
 | ||||
|    if ( *state->json == '-' ) | ||||
|       state->json++; | ||||
| 
 | ||||
|    if ( !isdigit( (unsigned char)*state->json ) ) | ||||
|       longjmp( state->env, JSONSAX_INVALID_VALUE ); | ||||
| 
 | ||||
|    skip_digits( state ); | ||||
| 
 | ||||
|    if ( *state->json == '.' ) | ||||
|    { | ||||
|       state->json++; | ||||
| 
 | ||||
|       if ( !isdigit( (unsigned char)*state->json ) ) | ||||
|          longjmp( state->env, JSONSAX_INVALID_VALUE ); | ||||
| 
 | ||||
|       skip_digits( state ); | ||||
|    } | ||||
| 
 | ||||
|    if ( *state->json == 'e' || *state->json == 'E' ) | ||||
|    { | ||||
|       state->json++; | ||||
| 
 | ||||
|       if ( *state->json == '-' || *state->json == '+' ) | ||||
|          state->json++; | ||||
| 
 | ||||
|       if ( !isdigit( (unsigned char)*state->json ) ) | ||||
|          longjmp( state->env, JSONSAX_INVALID_VALUE ); | ||||
| 
 | ||||
|       skip_digits( state ); | ||||
|    } | ||||
| 
 | ||||
|    HANDLE_2( number, number, state->json - number ); | ||||
| } | ||||
| 
 | ||||
| static void jsonx_parse_value(state_t* state) | ||||
| { | ||||
|    skip_spaces( state ); | ||||
| 
 | ||||
|    switch ( *state->json ) | ||||
|    { | ||||
|       case '{': | ||||
|          jsonx_parse_object(state); | ||||
|          break; | ||||
|       case '[': | ||||
|          jsonx_parse_array( state ); | ||||
|          break; | ||||
|       case '"': | ||||
|          jsonx_parse_string( state ); | ||||
|          break; | ||||
|       case 't': | ||||
|       case 'f': | ||||
|          jsonx_parse_boolean( state ); | ||||
|          break; | ||||
|       case 'n': | ||||
|          jsonx_parse_null( state ); | ||||
|          break; | ||||
|       case '0': | ||||
|       case '1': | ||||
|       case '2': | ||||
|       case '3': | ||||
|       case '4': | ||||
|       case '5': | ||||
|       case '6': | ||||
|       case '7': | ||||
|       case '8': | ||||
|       case '9': | ||||
|       case '-': | ||||
|          jsonx_parse_number( state ); | ||||
|          break; | ||||
| 
 | ||||
|       default: | ||||
|          longjmp( state->env, JSONSAX_INVALID_VALUE ); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| int jsonsax_parse( const char* json, const jsonsax_handlers_t* handlers, void* userdata ) | ||||
| { | ||||
|   state_t state; | ||||
|   int res; | ||||
| 
 | ||||
|   state.json = json; | ||||
|   state.handlers = handlers; | ||||
|   state.ud = userdata; | ||||
| 
 | ||||
|   if ( ( res = setjmp( state.env ) ) == 0 ) | ||||
|   { | ||||
|     if ( handlers->start_document ) | ||||
|       handlers->start_document( userdata ); | ||||
| 
 | ||||
|     jsonx_parse_value(&state); | ||||
| 
 | ||||
|     if ( handlers->end_document ) | ||||
|       handlers->end_document( userdata ); | ||||
| 
 | ||||
|     res = JSONSAX_OK; | ||||
|   } | ||||
| 
 | ||||
|   return res; | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,125 @@ | |||
| /* license:BSD-3-Clause
 | ||||
|  * copyright-holders:Aaron Giles | ||||
| *************************************************************************** | ||||
| 
 | ||||
|     bitstream.c | ||||
| 
 | ||||
|     Helper classes for reading/writing at the bit level. | ||||
| 
 | ||||
| ***************************************************************************/ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <libchdr/bitstream.h> | ||||
| 
 | ||||
| /***************************************************************************
 | ||||
|  *  INLINE FUNCTIONS | ||||
|  *************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| int bitstream_overflow(struct bitstream* bitstream) { return ((bitstream->doffset - bitstream->bits / 8) > bitstream->dlength); } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  create_bitstream - constructor | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| struct bitstream* create_bitstream(const void *src, uint32_t srclength) | ||||
| { | ||||
| 	struct bitstream* bitstream = (struct bitstream*)malloc(sizeof(struct bitstream)); | ||||
| 	bitstream->buffer = 0; | ||||
| 	bitstream->bits = 0; | ||||
| 	bitstream->read = (const uint8_t*)src; | ||||
| 	bitstream->doffset = 0; | ||||
| 	bitstream->dlength = srclength; | ||||
| 	return bitstream; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*-----------------------------------------------------
 | ||||
|  *  bitstream_peek - fetch the requested number of bits | ||||
|  *  but don't advance the input pointer | ||||
|  *----------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| uint32_t bitstream_peek(struct bitstream* bitstream, int numbits) | ||||
| { | ||||
| 	if (numbits == 0) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* fetch data if we need more */ | ||||
| 	if (numbits > bitstream->bits) | ||||
| 	{ | ||||
| 		while (bitstream->bits <= 24) | ||||
| 		{ | ||||
| 			if (bitstream->doffset < bitstream->dlength) | ||||
| 				bitstream->buffer |= bitstream->read[bitstream->doffset] << (24 - bitstream->bits); | ||||
| 			bitstream->doffset++; | ||||
| 			bitstream->bits += 8; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* return the data */ | ||||
| 	return bitstream->buffer >> (32 - numbits); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*-----------------------------------------------------
 | ||||
|  *  bitstream_remove - advance the input pointer by the | ||||
|  *  specified number of bits | ||||
|  *----------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| void bitstream_remove(struct bitstream* bitstream, int numbits) | ||||
| { | ||||
| 	bitstream->buffer <<= numbits; | ||||
| 	bitstream->bits -= numbits; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*-----------------------------------------------------
 | ||||
|  *  bitstream_read - fetch the requested number of bits | ||||
|  *----------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| uint32_t bitstream_read(struct bitstream* bitstream, int numbits) | ||||
| { | ||||
| 	uint32_t result = bitstream_peek(bitstream, numbits); | ||||
| 	bitstream_remove(bitstream, numbits); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  read_offset - return the current read offset | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| uint32_t bitstream_read_offset(struct bitstream* bitstream) | ||||
| { | ||||
| 	uint32_t result = bitstream->doffset; | ||||
| 	int bits = bitstream->bits; | ||||
| 	while (bits >= 8) | ||||
| 	{ | ||||
| 		result--; | ||||
| 		bits -= 8; | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  flush - flush to the nearest byte | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| uint32_t bitstream_flush(struct bitstream* bitstream) | ||||
| { | ||||
| 	while (bitstream->bits >= 8) | ||||
| 	{ | ||||
| 		bitstream->doffset--; | ||||
| 		bitstream->bits -= 8; | ||||
| 	} | ||||
| 	bitstream->bits = bitstream->buffer = 0; | ||||
| 	return bitstream->doffset; | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,422 @@ | |||
| /* license:BSD-3-Clause
 | ||||
|  * copyright-holders:Aaron Giles | ||||
| *************************************************************************** | ||||
| 
 | ||||
|     cdrom.c | ||||
| 
 | ||||
|     Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this | ||||
| 
 | ||||
| **************************************************************************** | ||||
| 
 | ||||
|     IMPORTANT: | ||||
|     "physical" block addresses are the actual addresses on the emulated CD. | ||||
|     "chd" block addresses are the block addresses in the CHD file. | ||||
|     Because we pad each track to a 4-frame boundary, these addressing | ||||
|     schemes will differ after track 1! | ||||
| 
 | ||||
| ***************************************************************************/ | ||||
| #ifdef WANT_RAW_DATA_SECTOR | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| 
 | ||||
| #include <libchdr/cdrom.h> | ||||
| 
 | ||||
| /***************************************************************************
 | ||||
|     DEBUGGING | ||||
| ***************************************************************************/ | ||||
| 
 | ||||
| /** @brief  The verbose. */ | ||||
| #define VERBOSE (0) | ||||
| #if VERBOSE | ||||
| 
 | ||||
| /**
 | ||||
|  * @def LOG(x) do | ||||
|  * | ||||
|  * @brief   A macro that defines log. | ||||
|  * | ||||
|  * @param   x   The void to process. | ||||
|  */ | ||||
| 
 | ||||
| #define LOG(x) do { if (VERBOSE) logerror x; } while (0) | ||||
| 
 | ||||
| /**
 | ||||
|  * @fn  void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2); | ||||
|  * | ||||
|  * @brief   Logerrors the given text. | ||||
|  * | ||||
|  * @param   text    The text. | ||||
|  * | ||||
|  * @return  A CLIB_DECL. | ||||
|  */ | ||||
| 
 | ||||
| void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2); | ||||
| #else | ||||
| 
 | ||||
| /**
 | ||||
|  * @def LOG(x); | ||||
|  * | ||||
|  * @brief   A macro that defines log. | ||||
|  * | ||||
|  * @param   x   The void to process. | ||||
|  */ | ||||
| 
 | ||||
| #define LOG(x) | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /***************************************************************************
 | ||||
|     CONSTANTS | ||||
| ***************************************************************************/ | ||||
| 
 | ||||
| /** @brief  offset within sector. */ | ||||
| #define SYNC_OFFSET 0x000 | ||||
| /** @brief  12 bytes. */ | ||||
| #define SYNC_NUM_BYTES 12 | ||||
| 
 | ||||
| /** @brief  offset within sector. */ | ||||
| #define MODE_OFFSET 0x00f | ||||
| 
 | ||||
| /** @brief  offset within sector. */ | ||||
| #define ECC_P_OFFSET 0x81c | ||||
| /** @brief  2 lots of 86. */ | ||||
| #define ECC_P_NUM_BYTES 86 | ||||
| /** @brief  24 bytes each. */ | ||||
| #define ECC_P_COMP 24 | ||||
| 
 | ||||
| /** @brief  The ECC q offset. */ | ||||
| #define ECC_Q_OFFSET (ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES) | ||||
| /** @brief  2 lots of 52. */ | ||||
| #define ECC_Q_NUM_BYTES 52 | ||||
| /** @brief  43 bytes each. */ | ||||
| #define ECC_Q_COMP 43 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief   ------------------------------------------------- | ||||
|  *            ECC lookup tables pre-calculated tables for ECC data calcs | ||||
|  *          -------------------------------------------------. | ||||
|  */ | ||||
| 
 | ||||
| static const uint8_t ecclow[256] = | ||||
| { | ||||
| 	0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, | ||||
| 	0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, | ||||
| 	0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, | ||||
| 	0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, | ||||
| 	0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, | ||||
| 	0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, | ||||
| 	0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, | ||||
| 	0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, | ||||
| 	0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0x0d, 0x0f, 0x09, 0x0b, 0x05, 0x07, 0x01, 0x03, | ||||
| 	0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23, | ||||
| 	0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43, | ||||
| 	0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63, | ||||
| 	0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83, | ||||
| 	0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3, | ||||
| 	0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3, | ||||
| 	0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3 | ||||
| }; | ||||
| 
 | ||||
| /** @brief  The ecchigh[ 256]. */ | ||||
| static const uint8_t ecchigh[256] = | ||||
| { | ||||
| 	0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05, | ||||
| 	0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe, | ||||
| 	0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee, | ||||
| 	0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15, | ||||
| 	0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce, | ||||
| 	0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35, | ||||
| 	0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25, | ||||
| 	0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde, | ||||
| 	0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e, | ||||
| 	0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75, | ||||
| 	0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65, | ||||
| 	0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e, | ||||
| 	0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45, | ||||
| 	0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe, | ||||
| 	0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae, | ||||
| 	0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief   ------------------------------------------------- | ||||
|  *            poffsets - each row represents the addresses used to calculate a byte of the ECC P | ||||
|  *            data 86 (*2) ECC P bytes, 24 values represented by each | ||||
|  *          -------------------------------------------------. | ||||
|  */ | ||||
| 
 | ||||
| static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] = | ||||
| { | ||||
| 	{ 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba }, | ||||
| 	{ 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb }, | ||||
| 	{ 0x002,0x058,0x0ae,0x104,0x15a,0x1b0,0x206,0x25c,0x2b2,0x308,0x35e,0x3b4,0x40a,0x460,0x4b6,0x50c,0x562,0x5b8,0x60e,0x664,0x6ba,0x710,0x766,0x7bc }, | ||||
| 	{ 0x003,0x059,0x0af,0x105,0x15b,0x1b1,0x207,0x25d,0x2b3,0x309,0x35f,0x3b5,0x40b,0x461,0x4b7,0x50d,0x563,0x5b9,0x60f,0x665,0x6bb,0x711,0x767,0x7bd }, | ||||
| 	{ 0x004,0x05a,0x0b0,0x106,0x15c,0x1b2,0x208,0x25e,0x2b4,0x30a,0x360,0x3b6,0x40c,0x462,0x4b8,0x50e,0x564,0x5ba,0x610,0x666,0x6bc,0x712,0x768,0x7be }, | ||||
| 	{ 0x005,0x05b,0x0b1,0x107,0x15d,0x1b3,0x209,0x25f,0x2b5,0x30b,0x361,0x3b7,0x40d,0x463,0x4b9,0x50f,0x565,0x5bb,0x611,0x667,0x6bd,0x713,0x769,0x7bf }, | ||||
| 	{ 0x006,0x05c,0x0b2,0x108,0x15e,0x1b4,0x20a,0x260,0x2b6,0x30c,0x362,0x3b8,0x40e,0x464,0x4ba,0x510,0x566,0x5bc,0x612,0x668,0x6be,0x714,0x76a,0x7c0 }, | ||||
| 	{ 0x007,0x05d,0x0b3,0x109,0x15f,0x1b5,0x20b,0x261,0x2b7,0x30d,0x363,0x3b9,0x40f,0x465,0x4bb,0x511,0x567,0x5bd,0x613,0x669,0x6bf,0x715,0x76b,0x7c1 }, | ||||
| 	{ 0x008,0x05e,0x0b4,0x10a,0x160,0x1b6,0x20c,0x262,0x2b8,0x30e,0x364,0x3ba,0x410,0x466,0x4bc,0x512,0x568,0x5be,0x614,0x66a,0x6c0,0x716,0x76c,0x7c2 }, | ||||
| 	{ 0x009,0x05f,0x0b5,0x10b,0x161,0x1b7,0x20d,0x263,0x2b9,0x30f,0x365,0x3bb,0x411,0x467,0x4bd,0x513,0x569,0x5bf,0x615,0x66b,0x6c1,0x717,0x76d,0x7c3 }, | ||||
| 	{ 0x00a,0x060,0x0b6,0x10c,0x162,0x1b8,0x20e,0x264,0x2ba,0x310,0x366,0x3bc,0x412,0x468,0x4be,0x514,0x56a,0x5c0,0x616,0x66c,0x6c2,0x718,0x76e,0x7c4 }, | ||||
| 	{ 0x00b,0x061,0x0b7,0x10d,0x163,0x1b9,0x20f,0x265,0x2bb,0x311,0x367,0x3bd,0x413,0x469,0x4bf,0x515,0x56b,0x5c1,0x617,0x66d,0x6c3,0x719,0x76f,0x7c5 }, | ||||
| 	{ 0x00c,0x062,0x0b8,0x10e,0x164,0x1ba,0x210,0x266,0x2bc,0x312,0x368,0x3be,0x414,0x46a,0x4c0,0x516,0x56c,0x5c2,0x618,0x66e,0x6c4,0x71a,0x770,0x7c6 }, | ||||
| 	{ 0x00d,0x063,0x0b9,0x10f,0x165,0x1bb,0x211,0x267,0x2bd,0x313,0x369,0x3bf,0x415,0x46b,0x4c1,0x517,0x56d,0x5c3,0x619,0x66f,0x6c5,0x71b,0x771,0x7c7 }, | ||||
| 	{ 0x00e,0x064,0x0ba,0x110,0x166,0x1bc,0x212,0x268,0x2be,0x314,0x36a,0x3c0,0x416,0x46c,0x4c2,0x518,0x56e,0x5c4,0x61a,0x670,0x6c6,0x71c,0x772,0x7c8 }, | ||||
| 	{ 0x00f,0x065,0x0bb,0x111,0x167,0x1bd,0x213,0x269,0x2bf,0x315,0x36b,0x3c1,0x417,0x46d,0x4c3,0x519,0x56f,0x5c5,0x61b,0x671,0x6c7,0x71d,0x773,0x7c9 }, | ||||
| 	{ 0x010,0x066,0x0bc,0x112,0x168,0x1be,0x214,0x26a,0x2c0,0x316,0x36c,0x3c2,0x418,0x46e,0x4c4,0x51a,0x570,0x5c6,0x61c,0x672,0x6c8,0x71e,0x774,0x7ca }, | ||||
| 	{ 0x011,0x067,0x0bd,0x113,0x169,0x1bf,0x215,0x26b,0x2c1,0x317,0x36d,0x3c3,0x419,0x46f,0x4c5,0x51b,0x571,0x5c7,0x61d,0x673,0x6c9,0x71f,0x775,0x7cb }, | ||||
| 	{ 0x012,0x068,0x0be,0x114,0x16a,0x1c0,0x216,0x26c,0x2c2,0x318,0x36e,0x3c4,0x41a,0x470,0x4c6,0x51c,0x572,0x5c8,0x61e,0x674,0x6ca,0x720,0x776,0x7cc }, | ||||
| 	{ 0x013,0x069,0x0bf,0x115,0x16b,0x1c1,0x217,0x26d,0x2c3,0x319,0x36f,0x3c5,0x41b,0x471,0x4c7,0x51d,0x573,0x5c9,0x61f,0x675,0x6cb,0x721,0x777,0x7cd }, | ||||
| 	{ 0x014,0x06a,0x0c0,0x116,0x16c,0x1c2,0x218,0x26e,0x2c4,0x31a,0x370,0x3c6,0x41c,0x472,0x4c8,0x51e,0x574,0x5ca,0x620,0x676,0x6cc,0x722,0x778,0x7ce }, | ||||
| 	{ 0x015,0x06b,0x0c1,0x117,0x16d,0x1c3,0x219,0x26f,0x2c5,0x31b,0x371,0x3c7,0x41d,0x473,0x4c9,0x51f,0x575,0x5cb,0x621,0x677,0x6cd,0x723,0x779,0x7cf }, | ||||
| 	{ 0x016,0x06c,0x0c2,0x118,0x16e,0x1c4,0x21a,0x270,0x2c6,0x31c,0x372,0x3c8,0x41e,0x474,0x4ca,0x520,0x576,0x5cc,0x622,0x678,0x6ce,0x724,0x77a,0x7d0 }, | ||||
| 	{ 0x017,0x06d,0x0c3,0x119,0x16f,0x1c5,0x21b,0x271,0x2c7,0x31d,0x373,0x3c9,0x41f,0x475,0x4cb,0x521,0x577,0x5cd,0x623,0x679,0x6cf,0x725,0x77b,0x7d1 }, | ||||
| 	{ 0x018,0x06e,0x0c4,0x11a,0x170,0x1c6,0x21c,0x272,0x2c8,0x31e,0x374,0x3ca,0x420,0x476,0x4cc,0x522,0x578,0x5ce,0x624,0x67a,0x6d0,0x726,0x77c,0x7d2 }, | ||||
| 	{ 0x019,0x06f,0x0c5,0x11b,0x171,0x1c7,0x21d,0x273,0x2c9,0x31f,0x375,0x3cb,0x421,0x477,0x4cd,0x523,0x579,0x5cf,0x625,0x67b,0x6d1,0x727,0x77d,0x7d3 }, | ||||
| 	{ 0x01a,0x070,0x0c6,0x11c,0x172,0x1c8,0x21e,0x274,0x2ca,0x320,0x376,0x3cc,0x422,0x478,0x4ce,0x524,0x57a,0x5d0,0x626,0x67c,0x6d2,0x728,0x77e,0x7d4 }, | ||||
| 	{ 0x01b,0x071,0x0c7,0x11d,0x173,0x1c9,0x21f,0x275,0x2cb,0x321,0x377,0x3cd,0x423,0x479,0x4cf,0x525,0x57b,0x5d1,0x627,0x67d,0x6d3,0x729,0x77f,0x7d5 }, | ||||
| 	{ 0x01c,0x072,0x0c8,0x11e,0x174,0x1ca,0x220,0x276,0x2cc,0x322,0x378,0x3ce,0x424,0x47a,0x4d0,0x526,0x57c,0x5d2,0x628,0x67e,0x6d4,0x72a,0x780,0x7d6 }, | ||||
| 	{ 0x01d,0x073,0x0c9,0x11f,0x175,0x1cb,0x221,0x277,0x2cd,0x323,0x379,0x3cf,0x425,0x47b,0x4d1,0x527,0x57d,0x5d3,0x629,0x67f,0x6d5,0x72b,0x781,0x7d7 }, | ||||
| 	{ 0x01e,0x074,0x0ca,0x120,0x176,0x1cc,0x222,0x278,0x2ce,0x324,0x37a,0x3d0,0x426,0x47c,0x4d2,0x528,0x57e,0x5d4,0x62a,0x680,0x6d6,0x72c,0x782,0x7d8 }, | ||||
| 	{ 0x01f,0x075,0x0cb,0x121,0x177,0x1cd,0x223,0x279,0x2cf,0x325,0x37b,0x3d1,0x427,0x47d,0x4d3,0x529,0x57f,0x5d5,0x62b,0x681,0x6d7,0x72d,0x783,0x7d9 }, | ||||
| 	{ 0x020,0x076,0x0cc,0x122,0x178,0x1ce,0x224,0x27a,0x2d0,0x326,0x37c,0x3d2,0x428,0x47e,0x4d4,0x52a,0x580,0x5d6,0x62c,0x682,0x6d8,0x72e,0x784,0x7da }, | ||||
| 	{ 0x021,0x077,0x0cd,0x123,0x179,0x1cf,0x225,0x27b,0x2d1,0x327,0x37d,0x3d3,0x429,0x47f,0x4d5,0x52b,0x581,0x5d7,0x62d,0x683,0x6d9,0x72f,0x785,0x7db }, | ||||
| 	{ 0x022,0x078,0x0ce,0x124,0x17a,0x1d0,0x226,0x27c,0x2d2,0x328,0x37e,0x3d4,0x42a,0x480,0x4d6,0x52c,0x582,0x5d8,0x62e,0x684,0x6da,0x730,0x786,0x7dc }, | ||||
| 	{ 0x023,0x079,0x0cf,0x125,0x17b,0x1d1,0x227,0x27d,0x2d3,0x329,0x37f,0x3d5,0x42b,0x481,0x4d7,0x52d,0x583,0x5d9,0x62f,0x685,0x6db,0x731,0x787,0x7dd }, | ||||
| 	{ 0x024,0x07a,0x0d0,0x126,0x17c,0x1d2,0x228,0x27e,0x2d4,0x32a,0x380,0x3d6,0x42c,0x482,0x4d8,0x52e,0x584,0x5da,0x630,0x686,0x6dc,0x732,0x788,0x7de }, | ||||
| 	{ 0x025,0x07b,0x0d1,0x127,0x17d,0x1d3,0x229,0x27f,0x2d5,0x32b,0x381,0x3d7,0x42d,0x483,0x4d9,0x52f,0x585,0x5db,0x631,0x687,0x6dd,0x733,0x789,0x7df }, | ||||
| 	{ 0x026,0x07c,0x0d2,0x128,0x17e,0x1d4,0x22a,0x280,0x2d6,0x32c,0x382,0x3d8,0x42e,0x484,0x4da,0x530,0x586,0x5dc,0x632,0x688,0x6de,0x734,0x78a,0x7e0 }, | ||||
| 	{ 0x027,0x07d,0x0d3,0x129,0x17f,0x1d5,0x22b,0x281,0x2d7,0x32d,0x383,0x3d9,0x42f,0x485,0x4db,0x531,0x587,0x5dd,0x633,0x689,0x6df,0x735,0x78b,0x7e1 }, | ||||
| 	{ 0x028,0x07e,0x0d4,0x12a,0x180,0x1d6,0x22c,0x282,0x2d8,0x32e,0x384,0x3da,0x430,0x486,0x4dc,0x532,0x588,0x5de,0x634,0x68a,0x6e0,0x736,0x78c,0x7e2 }, | ||||
| 	{ 0x029,0x07f,0x0d5,0x12b,0x181,0x1d7,0x22d,0x283,0x2d9,0x32f,0x385,0x3db,0x431,0x487,0x4dd,0x533,0x589,0x5df,0x635,0x68b,0x6e1,0x737,0x78d,0x7e3 }, | ||||
| 	{ 0x02a,0x080,0x0d6,0x12c,0x182,0x1d8,0x22e,0x284,0x2da,0x330,0x386,0x3dc,0x432,0x488,0x4de,0x534,0x58a,0x5e0,0x636,0x68c,0x6e2,0x738,0x78e,0x7e4 }, | ||||
| 	{ 0x02b,0x081,0x0d7,0x12d,0x183,0x1d9,0x22f,0x285,0x2db,0x331,0x387,0x3dd,0x433,0x489,0x4df,0x535,0x58b,0x5e1,0x637,0x68d,0x6e3,0x739,0x78f,0x7e5 }, | ||||
| 	{ 0x02c,0x082,0x0d8,0x12e,0x184,0x1da,0x230,0x286,0x2dc,0x332,0x388,0x3de,0x434,0x48a,0x4e0,0x536,0x58c,0x5e2,0x638,0x68e,0x6e4,0x73a,0x790,0x7e6 }, | ||||
| 	{ 0x02d,0x083,0x0d9,0x12f,0x185,0x1db,0x231,0x287,0x2dd,0x333,0x389,0x3df,0x435,0x48b,0x4e1,0x537,0x58d,0x5e3,0x639,0x68f,0x6e5,0x73b,0x791,0x7e7 }, | ||||
| 	{ 0x02e,0x084,0x0da,0x130,0x186,0x1dc,0x232,0x288,0x2de,0x334,0x38a,0x3e0,0x436,0x48c,0x4e2,0x538,0x58e,0x5e4,0x63a,0x690,0x6e6,0x73c,0x792,0x7e8 }, | ||||
| 	{ 0x02f,0x085,0x0db,0x131,0x187,0x1dd,0x233,0x289,0x2df,0x335,0x38b,0x3e1,0x437,0x48d,0x4e3,0x539,0x58f,0x5e5,0x63b,0x691,0x6e7,0x73d,0x793,0x7e9 }, | ||||
| 	{ 0x030,0x086,0x0dc,0x132,0x188,0x1de,0x234,0x28a,0x2e0,0x336,0x38c,0x3e2,0x438,0x48e,0x4e4,0x53a,0x590,0x5e6,0x63c,0x692,0x6e8,0x73e,0x794,0x7ea }, | ||||
| 	{ 0x031,0x087,0x0dd,0x133,0x189,0x1df,0x235,0x28b,0x2e1,0x337,0x38d,0x3e3,0x439,0x48f,0x4e5,0x53b,0x591,0x5e7,0x63d,0x693,0x6e9,0x73f,0x795,0x7eb }, | ||||
| 	{ 0x032,0x088,0x0de,0x134,0x18a,0x1e0,0x236,0x28c,0x2e2,0x338,0x38e,0x3e4,0x43a,0x490,0x4e6,0x53c,0x592,0x5e8,0x63e,0x694,0x6ea,0x740,0x796,0x7ec }, | ||||
| 	{ 0x033,0x089,0x0df,0x135,0x18b,0x1e1,0x237,0x28d,0x2e3,0x339,0x38f,0x3e5,0x43b,0x491,0x4e7,0x53d,0x593,0x5e9,0x63f,0x695,0x6eb,0x741,0x797,0x7ed }, | ||||
| 	{ 0x034,0x08a,0x0e0,0x136,0x18c,0x1e2,0x238,0x28e,0x2e4,0x33a,0x390,0x3e6,0x43c,0x492,0x4e8,0x53e,0x594,0x5ea,0x640,0x696,0x6ec,0x742,0x798,0x7ee }, | ||||
| 	{ 0x035,0x08b,0x0e1,0x137,0x18d,0x1e3,0x239,0x28f,0x2e5,0x33b,0x391,0x3e7,0x43d,0x493,0x4e9,0x53f,0x595,0x5eb,0x641,0x697,0x6ed,0x743,0x799,0x7ef }, | ||||
| 	{ 0x036,0x08c,0x0e2,0x138,0x18e,0x1e4,0x23a,0x290,0x2e6,0x33c,0x392,0x3e8,0x43e,0x494,0x4ea,0x540,0x596,0x5ec,0x642,0x698,0x6ee,0x744,0x79a,0x7f0 }, | ||||
| 	{ 0x037,0x08d,0x0e3,0x139,0x18f,0x1e5,0x23b,0x291,0x2e7,0x33d,0x393,0x3e9,0x43f,0x495,0x4eb,0x541,0x597,0x5ed,0x643,0x699,0x6ef,0x745,0x79b,0x7f1 }, | ||||
| 	{ 0x038,0x08e,0x0e4,0x13a,0x190,0x1e6,0x23c,0x292,0x2e8,0x33e,0x394,0x3ea,0x440,0x496,0x4ec,0x542,0x598,0x5ee,0x644,0x69a,0x6f0,0x746,0x79c,0x7f2 }, | ||||
| 	{ 0x039,0x08f,0x0e5,0x13b,0x191,0x1e7,0x23d,0x293,0x2e9,0x33f,0x395,0x3eb,0x441,0x497,0x4ed,0x543,0x599,0x5ef,0x645,0x69b,0x6f1,0x747,0x79d,0x7f3 }, | ||||
| 	{ 0x03a,0x090,0x0e6,0x13c,0x192,0x1e8,0x23e,0x294,0x2ea,0x340,0x396,0x3ec,0x442,0x498,0x4ee,0x544,0x59a,0x5f0,0x646,0x69c,0x6f2,0x748,0x79e,0x7f4 }, | ||||
| 	{ 0x03b,0x091,0x0e7,0x13d,0x193,0x1e9,0x23f,0x295,0x2eb,0x341,0x397,0x3ed,0x443,0x499,0x4ef,0x545,0x59b,0x5f1,0x647,0x69d,0x6f3,0x749,0x79f,0x7f5 }, | ||||
| 	{ 0x03c,0x092,0x0e8,0x13e,0x194,0x1ea,0x240,0x296,0x2ec,0x342,0x398,0x3ee,0x444,0x49a,0x4f0,0x546,0x59c,0x5f2,0x648,0x69e,0x6f4,0x74a,0x7a0,0x7f6 }, | ||||
| 	{ 0x03d,0x093,0x0e9,0x13f,0x195,0x1eb,0x241,0x297,0x2ed,0x343,0x399,0x3ef,0x445,0x49b,0x4f1,0x547,0x59d,0x5f3,0x649,0x69f,0x6f5,0x74b,0x7a1,0x7f7 }, | ||||
| 	{ 0x03e,0x094,0x0ea,0x140,0x196,0x1ec,0x242,0x298,0x2ee,0x344,0x39a,0x3f0,0x446,0x49c,0x4f2,0x548,0x59e,0x5f4,0x64a,0x6a0,0x6f6,0x74c,0x7a2,0x7f8 }, | ||||
| 	{ 0x03f,0x095,0x0eb,0x141,0x197,0x1ed,0x243,0x299,0x2ef,0x345,0x39b,0x3f1,0x447,0x49d,0x4f3,0x549,0x59f,0x5f5,0x64b,0x6a1,0x6f7,0x74d,0x7a3,0x7f9 }, | ||||
| 	{ 0x040,0x096,0x0ec,0x142,0x198,0x1ee,0x244,0x29a,0x2f0,0x346,0x39c,0x3f2,0x448,0x49e,0x4f4,0x54a,0x5a0,0x5f6,0x64c,0x6a2,0x6f8,0x74e,0x7a4,0x7fa }, | ||||
| 	{ 0x041,0x097,0x0ed,0x143,0x199,0x1ef,0x245,0x29b,0x2f1,0x347,0x39d,0x3f3,0x449,0x49f,0x4f5,0x54b,0x5a1,0x5f7,0x64d,0x6a3,0x6f9,0x74f,0x7a5,0x7fb }, | ||||
| 	{ 0x042,0x098,0x0ee,0x144,0x19a,0x1f0,0x246,0x29c,0x2f2,0x348,0x39e,0x3f4,0x44a,0x4a0,0x4f6,0x54c,0x5a2,0x5f8,0x64e,0x6a4,0x6fa,0x750,0x7a6,0x7fc }, | ||||
| 	{ 0x043,0x099,0x0ef,0x145,0x19b,0x1f1,0x247,0x29d,0x2f3,0x349,0x39f,0x3f5,0x44b,0x4a1,0x4f7,0x54d,0x5a3,0x5f9,0x64f,0x6a5,0x6fb,0x751,0x7a7,0x7fd }, | ||||
| 	{ 0x044,0x09a,0x0f0,0x146,0x19c,0x1f2,0x248,0x29e,0x2f4,0x34a,0x3a0,0x3f6,0x44c,0x4a2,0x4f8,0x54e,0x5a4,0x5fa,0x650,0x6a6,0x6fc,0x752,0x7a8,0x7fe }, | ||||
| 	{ 0x045,0x09b,0x0f1,0x147,0x19d,0x1f3,0x249,0x29f,0x2f5,0x34b,0x3a1,0x3f7,0x44d,0x4a3,0x4f9,0x54f,0x5a5,0x5fb,0x651,0x6a7,0x6fd,0x753,0x7a9,0x7ff }, | ||||
| 	{ 0x046,0x09c,0x0f2,0x148,0x19e,0x1f4,0x24a,0x2a0,0x2f6,0x34c,0x3a2,0x3f8,0x44e,0x4a4,0x4fa,0x550,0x5a6,0x5fc,0x652,0x6a8,0x6fe,0x754,0x7aa,0x800 }, | ||||
| 	{ 0x047,0x09d,0x0f3,0x149,0x19f,0x1f5,0x24b,0x2a1,0x2f7,0x34d,0x3a3,0x3f9,0x44f,0x4a5,0x4fb,0x551,0x5a7,0x5fd,0x653,0x6a9,0x6ff,0x755,0x7ab,0x801 }, | ||||
| 	{ 0x048,0x09e,0x0f4,0x14a,0x1a0,0x1f6,0x24c,0x2a2,0x2f8,0x34e,0x3a4,0x3fa,0x450,0x4a6,0x4fc,0x552,0x5a8,0x5fe,0x654,0x6aa,0x700,0x756,0x7ac,0x802 }, | ||||
| 	{ 0x049,0x09f,0x0f5,0x14b,0x1a1,0x1f7,0x24d,0x2a3,0x2f9,0x34f,0x3a5,0x3fb,0x451,0x4a7,0x4fd,0x553,0x5a9,0x5ff,0x655,0x6ab,0x701,0x757,0x7ad,0x803 }, | ||||
| 	{ 0x04a,0x0a0,0x0f6,0x14c,0x1a2,0x1f8,0x24e,0x2a4,0x2fa,0x350,0x3a6,0x3fc,0x452,0x4a8,0x4fe,0x554,0x5aa,0x600,0x656,0x6ac,0x702,0x758,0x7ae,0x804 }, | ||||
| 	{ 0x04b,0x0a1,0x0f7,0x14d,0x1a3,0x1f9,0x24f,0x2a5,0x2fb,0x351,0x3a7,0x3fd,0x453,0x4a9,0x4ff,0x555,0x5ab,0x601,0x657,0x6ad,0x703,0x759,0x7af,0x805 }, | ||||
| 	{ 0x04c,0x0a2,0x0f8,0x14e,0x1a4,0x1fa,0x250,0x2a6,0x2fc,0x352,0x3a8,0x3fe,0x454,0x4aa,0x500,0x556,0x5ac,0x602,0x658,0x6ae,0x704,0x75a,0x7b0,0x806 }, | ||||
| 	{ 0x04d,0x0a3,0x0f9,0x14f,0x1a5,0x1fb,0x251,0x2a7,0x2fd,0x353,0x3a9,0x3ff,0x455,0x4ab,0x501,0x557,0x5ad,0x603,0x659,0x6af,0x705,0x75b,0x7b1,0x807 }, | ||||
| 	{ 0x04e,0x0a4,0x0fa,0x150,0x1a6,0x1fc,0x252,0x2a8,0x2fe,0x354,0x3aa,0x400,0x456,0x4ac,0x502,0x558,0x5ae,0x604,0x65a,0x6b0,0x706,0x75c,0x7b2,0x808 }, | ||||
| 	{ 0x04f,0x0a5,0x0fb,0x151,0x1a7,0x1fd,0x253,0x2a9,0x2ff,0x355,0x3ab,0x401,0x457,0x4ad,0x503,0x559,0x5af,0x605,0x65b,0x6b1,0x707,0x75d,0x7b3,0x809 }, | ||||
| 	{ 0x050,0x0a6,0x0fc,0x152,0x1a8,0x1fe,0x254,0x2aa,0x300,0x356,0x3ac,0x402,0x458,0x4ae,0x504,0x55a,0x5b0,0x606,0x65c,0x6b2,0x708,0x75e,0x7b4,0x80a }, | ||||
| 	{ 0x051,0x0a7,0x0fd,0x153,0x1a9,0x1ff,0x255,0x2ab,0x301,0x357,0x3ad,0x403,0x459,0x4af,0x505,0x55b,0x5b1,0x607,0x65d,0x6b3,0x709,0x75f,0x7b5,0x80b }, | ||||
| 	{ 0x052,0x0a8,0x0fe,0x154,0x1aa,0x200,0x256,0x2ac,0x302,0x358,0x3ae,0x404,0x45a,0x4b0,0x506,0x55c,0x5b2,0x608,0x65e,0x6b4,0x70a,0x760,0x7b6,0x80c }, | ||||
| 	{ 0x053,0x0a9,0x0ff,0x155,0x1ab,0x201,0x257,0x2ad,0x303,0x359,0x3af,0x405,0x45b,0x4b1,0x507,0x55d,0x5b3,0x609,0x65f,0x6b5,0x70b,0x761,0x7b7,0x80d }, | ||||
| 	{ 0x054,0x0aa,0x100,0x156,0x1ac,0x202,0x258,0x2ae,0x304,0x35a,0x3b0,0x406,0x45c,0x4b2,0x508,0x55e,0x5b4,0x60a,0x660,0x6b6,0x70c,0x762,0x7b8,0x80e }, | ||||
| 	{ 0x055,0x0ab,0x101,0x157,0x1ad,0x203,0x259,0x2af,0x305,0x35b,0x3b1,0x407,0x45d,0x4b3,0x509,0x55f,0x5b5,0x60b,0x661,0x6b7,0x70d,0x763,0x7b9,0x80f } | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief   ------------------------------------------------- | ||||
|  *            qoffsets - each row represents the addresses used to calculate a byte of the ECC Q | ||||
|  *            data 52 (*2) ECC Q bytes, 43 values represented by each | ||||
|  *          -------------------------------------------------. | ||||
|  */ | ||||
| 
 | ||||
| static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] = | ||||
| { | ||||
| 	{ 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 }, | ||||
| 	{ 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 }, | ||||
| 	{ 0x056,0x0ae,0x106,0x15e,0x1b6,0x20e,0x266,0x2be,0x316,0x36e,0x3c6,0x41e,0x476,0x4ce,0x526,0x57e,0x5d6,0x62e,0x686,0x6de,0x736,0x78e,0x7e6,0x83e,0x896,0x032,0x08a,0x0e2,0x13a,0x192,0x1ea,0x242,0x29a,0x2f2,0x34a,0x3a2,0x3fa,0x452,0x4aa,0x502,0x55a,0x5b2,0x60a }, | ||||
| 	{ 0x057,0x0af,0x107,0x15f,0x1b7,0x20f,0x267,0x2bf,0x317,0x36f,0x3c7,0x41f,0x477,0x4cf,0x527,0x57f,0x5d7,0x62f,0x687,0x6df,0x737,0x78f,0x7e7,0x83f,0x897,0x033,0x08b,0x0e3,0x13b,0x193,0x1eb,0x243,0x29b,0x2f3,0x34b,0x3a3,0x3fb,0x453,0x4ab,0x503,0x55b,0x5b3,0x60b }, | ||||
| 	{ 0x0ac,0x104,0x15c,0x1b4,0x20c,0x264,0x2bc,0x314,0x36c,0x3c4,0x41c,0x474,0x4cc,0x524,0x57c,0x5d4,0x62c,0x684,0x6dc,0x734,0x78c,0x7e4,0x83c,0x894,0x030,0x088,0x0e0,0x138,0x190,0x1e8,0x240,0x298,0x2f0,0x348,0x3a0,0x3f8,0x450,0x4a8,0x500,0x558,0x5b0,0x608,0x660 }, | ||||
| 	{ 0x0ad,0x105,0x15d,0x1b5,0x20d,0x265,0x2bd,0x315,0x36d,0x3c5,0x41d,0x475,0x4cd,0x525,0x57d,0x5d5,0x62d,0x685,0x6dd,0x735,0x78d,0x7e5,0x83d,0x895,0x031,0x089,0x0e1,0x139,0x191,0x1e9,0x241,0x299,0x2f1,0x349,0x3a1,0x3f9,0x451,0x4a9,0x501,0x559,0x5b1,0x609,0x661 }, | ||||
| 	{ 0x102,0x15a,0x1b2,0x20a,0x262,0x2ba,0x312,0x36a,0x3c2,0x41a,0x472,0x4ca,0x522,0x57a,0x5d2,0x62a,0x682,0x6da,0x732,0x78a,0x7e2,0x83a,0x892,0x02e,0x086,0x0de,0x136,0x18e,0x1e6,0x23e,0x296,0x2ee,0x346,0x39e,0x3f6,0x44e,0x4a6,0x4fe,0x556,0x5ae,0x606,0x65e,0x6b6 }, | ||||
| 	{ 0x103,0x15b,0x1b3,0x20b,0x263,0x2bb,0x313,0x36b,0x3c3,0x41b,0x473,0x4cb,0x523,0x57b,0x5d3,0x62b,0x683,0x6db,0x733,0x78b,0x7e3,0x83b,0x893,0x02f,0x087,0x0df,0x137,0x18f,0x1e7,0x23f,0x297,0x2ef,0x347,0x39f,0x3f7,0x44f,0x4a7,0x4ff,0x557,0x5af,0x607,0x65f,0x6b7 }, | ||||
| 	{ 0x158,0x1b0,0x208,0x260,0x2b8,0x310,0x368,0x3c0,0x418,0x470,0x4c8,0x520,0x578,0x5d0,0x628,0x680,0x6d8,0x730,0x788,0x7e0,0x838,0x890,0x02c,0x084,0x0dc,0x134,0x18c,0x1e4,0x23c,0x294,0x2ec,0x344,0x39c,0x3f4,0x44c,0x4a4,0x4fc,0x554,0x5ac,0x604,0x65c,0x6b4,0x70c }, | ||||
| 	{ 0x159,0x1b1,0x209,0x261,0x2b9,0x311,0x369,0x3c1,0x419,0x471,0x4c9,0x521,0x579,0x5d1,0x629,0x681,0x6d9,0x731,0x789,0x7e1,0x839,0x891,0x02d,0x085,0x0dd,0x135,0x18d,0x1e5,0x23d,0x295,0x2ed,0x345,0x39d,0x3f5,0x44d,0x4a5,0x4fd,0x555,0x5ad,0x605,0x65d,0x6b5,0x70d }, | ||||
| 	{ 0x1ae,0x206,0x25e,0x2b6,0x30e,0x366,0x3be,0x416,0x46e,0x4c6,0x51e,0x576,0x5ce,0x626,0x67e,0x6d6,0x72e,0x786,0x7de,0x836,0x88e,0x02a,0x082,0x0da,0x132,0x18a,0x1e2,0x23a,0x292,0x2ea,0x342,0x39a,0x3f2,0x44a,0x4a2,0x4fa,0x552,0x5aa,0x602,0x65a,0x6b2,0x70a,0x762 }, | ||||
| 	{ 0x1af,0x207,0x25f,0x2b7,0x30f,0x367,0x3bf,0x417,0x46f,0x4c7,0x51f,0x577,0x5cf,0x627,0x67f,0x6d7,0x72f,0x787,0x7df,0x837,0x88f,0x02b,0x083,0x0db,0x133,0x18b,0x1e3,0x23b,0x293,0x2eb,0x343,0x39b,0x3f3,0x44b,0x4a3,0x4fb,0x553,0x5ab,0x603,0x65b,0x6b3,0x70b,0x763 }, | ||||
| 	{ 0x204,0x25c,0x2b4,0x30c,0x364,0x3bc,0x414,0x46c,0x4c4,0x51c,0x574,0x5cc,0x624,0x67c,0x6d4,0x72c,0x784,0x7dc,0x834,0x88c,0x028,0x080,0x0d8,0x130,0x188,0x1e0,0x238,0x290,0x2e8,0x340,0x398,0x3f0,0x448,0x4a0,0x4f8,0x550,0x5a8,0x600,0x658,0x6b0,0x708,0x760,0x7b8 }, | ||||
| 	{ 0x205,0x25d,0x2b5,0x30d,0x365,0x3bd,0x415,0x46d,0x4c5,0x51d,0x575,0x5cd,0x625,0x67d,0x6d5,0x72d,0x785,0x7dd,0x835,0x88d,0x029,0x081,0x0d9,0x131,0x189,0x1e1,0x239,0x291,0x2e9,0x341,0x399,0x3f1,0x449,0x4a1,0x4f9,0x551,0x5a9,0x601,0x659,0x6b1,0x709,0x761,0x7b9 }, | ||||
| 	{ 0x25a,0x2b2,0x30a,0x362,0x3ba,0x412,0x46a,0x4c2,0x51a,0x572,0x5ca,0x622,0x67a,0x6d2,0x72a,0x782,0x7da,0x832,0x88a,0x026,0x07e,0x0d6,0x12e,0x186,0x1de,0x236,0x28e,0x2e6,0x33e,0x396,0x3ee,0x446,0x49e,0x4f6,0x54e,0x5a6,0x5fe,0x656,0x6ae,0x706,0x75e,0x7b6,0x80e }, | ||||
| 	{ 0x25b,0x2b3,0x30b,0x363,0x3bb,0x413,0x46b,0x4c3,0x51b,0x573,0x5cb,0x623,0x67b,0x6d3,0x72b,0x783,0x7db,0x833,0x88b,0x027,0x07f,0x0d7,0x12f,0x187,0x1df,0x237,0x28f,0x2e7,0x33f,0x397,0x3ef,0x447,0x49f,0x4f7,0x54f,0x5a7,0x5ff,0x657,0x6af,0x707,0x75f,0x7b7,0x80f }, | ||||
| 	{ 0x2b0,0x308,0x360,0x3b8,0x410,0x468,0x4c0,0x518,0x570,0x5c8,0x620,0x678,0x6d0,0x728,0x780,0x7d8,0x830,0x888,0x024,0x07c,0x0d4,0x12c,0x184,0x1dc,0x234,0x28c,0x2e4,0x33c,0x394,0x3ec,0x444,0x49c,0x4f4,0x54c,0x5a4,0x5fc,0x654,0x6ac,0x704,0x75c,0x7b4,0x80c,0x864 }, | ||||
| 	{ 0x2b1,0x309,0x361,0x3b9,0x411,0x469,0x4c1,0x519,0x571,0x5c9,0x621,0x679,0x6d1,0x729,0x781,0x7d9,0x831,0x889,0x025,0x07d,0x0d5,0x12d,0x185,0x1dd,0x235,0x28d,0x2e5,0x33d,0x395,0x3ed,0x445,0x49d,0x4f5,0x54d,0x5a5,0x5fd,0x655,0x6ad,0x705,0x75d,0x7b5,0x80d,0x865 }, | ||||
| 	{ 0x306,0x35e,0x3b6,0x40e,0x466,0x4be,0x516,0x56e,0x5c6,0x61e,0x676,0x6ce,0x726,0x77e,0x7d6,0x82e,0x886,0x022,0x07a,0x0d2,0x12a,0x182,0x1da,0x232,0x28a,0x2e2,0x33a,0x392,0x3ea,0x442,0x49a,0x4f2,0x54a,0x5a2,0x5fa,0x652,0x6aa,0x702,0x75a,0x7b2,0x80a,0x862,0x8ba }, | ||||
| 	{ 0x307,0x35f,0x3b7,0x40f,0x467,0x4bf,0x517,0x56f,0x5c7,0x61f,0x677,0x6cf,0x727,0x77f,0x7d7,0x82f,0x887,0x023,0x07b,0x0d3,0x12b,0x183,0x1db,0x233,0x28b,0x2e3,0x33b,0x393,0x3eb,0x443,0x49b,0x4f3,0x54b,0x5a3,0x5fb,0x653,0x6ab,0x703,0x75b,0x7b3,0x80b,0x863,0x8bb }, | ||||
| 	{ 0x35c,0x3b4,0x40c,0x464,0x4bc,0x514,0x56c,0x5c4,0x61c,0x674,0x6cc,0x724,0x77c,0x7d4,0x82c,0x884,0x020,0x078,0x0d0,0x128,0x180,0x1d8,0x230,0x288,0x2e0,0x338,0x390,0x3e8,0x440,0x498,0x4f0,0x548,0x5a0,0x5f8,0x650,0x6a8,0x700,0x758,0x7b0,0x808,0x860,0x8b8,0x054 }, | ||||
| 	{ 0x35d,0x3b5,0x40d,0x465,0x4bd,0x515,0x56d,0x5c5,0x61d,0x675,0x6cd,0x725,0x77d,0x7d5,0x82d,0x885,0x021,0x079,0x0d1,0x129,0x181,0x1d9,0x231,0x289,0x2e1,0x339,0x391,0x3e9,0x441,0x499,0x4f1,0x549,0x5a1,0x5f9,0x651,0x6a9,0x701,0x759,0x7b1,0x809,0x861,0x8b9,0x055 }, | ||||
| 	{ 0x3b2,0x40a,0x462,0x4ba,0x512,0x56a,0x5c2,0x61a,0x672,0x6ca,0x722,0x77a,0x7d2,0x82a,0x882,0x01e,0x076,0x0ce,0x126,0x17e,0x1d6,0x22e,0x286,0x2de,0x336,0x38e,0x3e6,0x43e,0x496,0x4ee,0x546,0x59e,0x5f6,0x64e,0x6a6,0x6fe,0x756,0x7ae,0x806,0x85e,0x8b6,0x052,0x0aa }, | ||||
| 	{ 0x3b3,0x40b,0x463,0x4bb,0x513,0x56b,0x5c3,0x61b,0x673,0x6cb,0x723,0x77b,0x7d3,0x82b,0x883,0x01f,0x077,0x0cf,0x127,0x17f,0x1d7,0x22f,0x287,0x2df,0x337,0x38f,0x3e7,0x43f,0x497,0x4ef,0x547,0x59f,0x5f7,0x64f,0x6a7,0x6ff,0x757,0x7af,0x807,0x85f,0x8b7,0x053,0x0ab }, | ||||
| 	{ 0x408,0x460,0x4b8,0x510,0x568,0x5c0,0x618,0x670,0x6c8,0x720,0x778,0x7d0,0x828,0x880,0x01c,0x074,0x0cc,0x124,0x17c,0x1d4,0x22c,0x284,0x2dc,0x334,0x38c,0x3e4,0x43c,0x494,0x4ec,0x544,0x59c,0x5f4,0x64c,0x6a4,0x6fc,0x754,0x7ac,0x804,0x85c,0x8b4,0x050,0x0a8,0x100 }, | ||||
| 	{ 0x409,0x461,0x4b9,0x511,0x569,0x5c1,0x619,0x671,0x6c9,0x721,0x779,0x7d1,0x829,0x881,0x01d,0x075,0x0cd,0x125,0x17d,0x1d5,0x22d,0x285,0x2dd,0x335,0x38d,0x3e5,0x43d,0x495,0x4ed,0x545,0x59d,0x5f5,0x64d,0x6a5,0x6fd,0x755,0x7ad,0x805,0x85d,0x8b5,0x051,0x0a9,0x101 }, | ||||
| 	{ 0x45e,0x4b6,0x50e,0x566,0x5be,0x616,0x66e,0x6c6,0x71e,0x776,0x7ce,0x826,0x87e,0x01a,0x072,0x0ca,0x122,0x17a,0x1d2,0x22a,0x282,0x2da,0x332,0x38a,0x3e2,0x43a,0x492,0x4ea,0x542,0x59a,0x5f2,0x64a,0x6a2,0x6fa,0x752,0x7aa,0x802,0x85a,0x8b2,0x04e,0x0a6,0x0fe,0x156 }, | ||||
| 	{ 0x45f,0x4b7,0x50f,0x567,0x5bf,0x617,0x66f,0x6c7,0x71f,0x777,0x7cf,0x827,0x87f,0x01b,0x073,0x0cb,0x123,0x17b,0x1d3,0x22b,0x283,0x2db,0x333,0x38b,0x3e3,0x43b,0x493,0x4eb,0x543,0x59b,0x5f3,0x64b,0x6a3,0x6fb,0x753,0x7ab,0x803,0x85b,0x8b3,0x04f,0x0a7,0x0ff,0x157 }, | ||||
| 	{ 0x4b4,0x50c,0x564,0x5bc,0x614,0x66c,0x6c4,0x71c,0x774,0x7cc,0x824,0x87c,0x018,0x070,0x0c8,0x120,0x178,0x1d0,0x228,0x280,0x2d8,0x330,0x388,0x3e0,0x438,0x490,0x4e8,0x540,0x598,0x5f0,0x648,0x6a0,0x6f8,0x750,0x7a8,0x800,0x858,0x8b0,0x04c,0x0a4,0x0fc,0x154,0x1ac }, | ||||
| 	{ 0x4b5,0x50d,0x565,0x5bd,0x615,0x66d,0x6c5,0x71d,0x775,0x7cd,0x825,0x87d,0x019,0x071,0x0c9,0x121,0x179,0x1d1,0x229,0x281,0x2d9,0x331,0x389,0x3e1,0x439,0x491,0x4e9,0x541,0x599,0x5f1,0x649,0x6a1,0x6f9,0x751,0x7a9,0x801,0x859,0x8b1,0x04d,0x0a5,0x0fd,0x155,0x1ad }, | ||||
| 	{ 0x50a,0x562,0x5ba,0x612,0x66a,0x6c2,0x71a,0x772,0x7ca,0x822,0x87a,0x016,0x06e,0x0c6,0x11e,0x176,0x1ce,0x226,0x27e,0x2d6,0x32e,0x386,0x3de,0x436,0x48e,0x4e6,0x53e,0x596,0x5ee,0x646,0x69e,0x6f6,0x74e,0x7a6,0x7fe,0x856,0x8ae,0x04a,0x0a2,0x0fa,0x152,0x1aa,0x202 }, | ||||
| 	{ 0x50b,0x563,0x5bb,0x613,0x66b,0x6c3,0x71b,0x773,0x7cb,0x823,0x87b,0x017,0x06f,0x0c7,0x11f,0x177,0x1cf,0x227,0x27f,0x2d7,0x32f,0x387,0x3df,0x437,0x48f,0x4e7,0x53f,0x597,0x5ef,0x647,0x69f,0x6f7,0x74f,0x7a7,0x7ff,0x857,0x8af,0x04b,0x0a3,0x0fb,0x153,0x1ab,0x203 }, | ||||
| 	{ 0x560,0x5b8,0x610,0x668,0x6c0,0x718,0x770,0x7c8,0x820,0x878,0x014,0x06c,0x0c4,0x11c,0x174,0x1cc,0x224,0x27c,0x2d4,0x32c,0x384,0x3dc,0x434,0x48c,0x4e4,0x53c,0x594,0x5ec,0x644,0x69c,0x6f4,0x74c,0x7a4,0x7fc,0x854,0x8ac,0x048,0x0a0,0x0f8,0x150,0x1a8,0x200,0x258 }, | ||||
| 	{ 0x561,0x5b9,0x611,0x669,0x6c1,0x719,0x771,0x7c9,0x821,0x879,0x015,0x06d,0x0c5,0x11d,0x175,0x1cd,0x225,0x27d,0x2d5,0x32d,0x385,0x3dd,0x435,0x48d,0x4e5,0x53d,0x595,0x5ed,0x645,0x69d,0x6f5,0x74d,0x7a5,0x7fd,0x855,0x8ad,0x049,0x0a1,0x0f9,0x151,0x1a9,0x201,0x259 }, | ||||
| 	{ 0x5b6,0x60e,0x666,0x6be,0x716,0x76e,0x7c6,0x81e,0x876,0x012,0x06a,0x0c2,0x11a,0x172,0x1ca,0x222,0x27a,0x2d2,0x32a,0x382,0x3da,0x432,0x48a,0x4e2,0x53a,0x592,0x5ea,0x642,0x69a,0x6f2,0x74a,0x7a2,0x7fa,0x852,0x8aa,0x046,0x09e,0x0f6,0x14e,0x1a6,0x1fe,0x256,0x2ae }, | ||||
| 	{ 0x5b7,0x60f,0x667,0x6bf,0x717,0x76f,0x7c7,0x81f,0x877,0x013,0x06b,0x0c3,0x11b,0x173,0x1cb,0x223,0x27b,0x2d3,0x32b,0x383,0x3db,0x433,0x48b,0x4e3,0x53b,0x593,0x5eb,0x643,0x69b,0x6f3,0x74b,0x7a3,0x7fb,0x853,0x8ab,0x047,0x09f,0x0f7,0x14f,0x1a7,0x1ff,0x257,0x2af }, | ||||
| 	{ 0x60c,0x664,0x6bc,0x714,0x76c,0x7c4,0x81c,0x874,0x010,0x068,0x0c0,0x118,0x170,0x1c8,0x220,0x278,0x2d0,0x328,0x380,0x3d8,0x430,0x488,0x4e0,0x538,0x590,0x5e8,0x640,0x698,0x6f0,0x748,0x7a0,0x7f8,0x850,0x8a8,0x044,0x09c,0x0f4,0x14c,0x1a4,0x1fc,0x254,0x2ac,0x304 }, | ||||
| 	{ 0x60d,0x665,0x6bd,0x715,0x76d,0x7c5,0x81d,0x875,0x011,0x069,0x0c1,0x119,0x171,0x1c9,0x221,0x279,0x2d1,0x329,0x381,0x3d9,0x431,0x489,0x4e1,0x539,0x591,0x5e9,0x641,0x699,0x6f1,0x749,0x7a1,0x7f9,0x851,0x8a9,0x045,0x09d,0x0f5,0x14d,0x1a5,0x1fd,0x255,0x2ad,0x305 }, | ||||
| 	{ 0x662,0x6ba,0x712,0x76a,0x7c2,0x81a,0x872,0x00e,0x066,0x0be,0x116,0x16e,0x1c6,0x21e,0x276,0x2ce,0x326,0x37e,0x3d6,0x42e,0x486,0x4de,0x536,0x58e,0x5e6,0x63e,0x696,0x6ee,0x746,0x79e,0x7f6,0x84e,0x8a6,0x042,0x09a,0x0f2,0x14a,0x1a2,0x1fa,0x252,0x2aa,0x302,0x35a }, | ||||
| 	{ 0x663,0x6bb,0x713,0x76b,0x7c3,0x81b,0x873,0x00f,0x067,0x0bf,0x117,0x16f,0x1c7,0x21f,0x277,0x2cf,0x327,0x37f,0x3d7,0x42f,0x487,0x4df,0x537,0x58f,0x5e7,0x63f,0x697,0x6ef,0x747,0x79f,0x7f7,0x84f,0x8a7,0x043,0x09b,0x0f3,0x14b,0x1a3,0x1fb,0x253,0x2ab,0x303,0x35b }, | ||||
| 	{ 0x6b8,0x710,0x768,0x7c0,0x818,0x870,0x00c,0x064,0x0bc,0x114,0x16c,0x1c4,0x21c,0x274,0x2cc,0x324,0x37c,0x3d4,0x42c,0x484,0x4dc,0x534,0x58c,0x5e4,0x63c,0x694,0x6ec,0x744,0x79c,0x7f4,0x84c,0x8a4,0x040,0x098,0x0f0,0x148,0x1a0,0x1f8,0x250,0x2a8,0x300,0x358,0x3b0 }, | ||||
| 	{ 0x6b9,0x711,0x769,0x7c1,0x819,0x871,0x00d,0x065,0x0bd,0x115,0x16d,0x1c5,0x21d,0x275,0x2cd,0x325,0x37d,0x3d5,0x42d,0x485,0x4dd,0x535,0x58d,0x5e5,0x63d,0x695,0x6ed,0x745,0x79d,0x7f5,0x84d,0x8a5,0x041,0x099,0x0f1,0x149,0x1a1,0x1f9,0x251,0x2a9,0x301,0x359,0x3b1 }, | ||||
| 	{ 0x70e,0x766,0x7be,0x816,0x86e,0x00a,0x062,0x0ba,0x112,0x16a,0x1c2,0x21a,0x272,0x2ca,0x322,0x37a,0x3d2,0x42a,0x482,0x4da,0x532,0x58a,0x5e2,0x63a,0x692,0x6ea,0x742,0x79a,0x7f2,0x84a,0x8a2,0x03e,0x096,0x0ee,0x146,0x19e,0x1f6,0x24e,0x2a6,0x2fe,0x356,0x3ae,0x406 }, | ||||
| 	{ 0x70f,0x767,0x7bf,0x817,0x86f,0x00b,0x063,0x0bb,0x113,0x16b,0x1c3,0x21b,0x273,0x2cb,0x323,0x37b,0x3d3,0x42b,0x483,0x4db,0x533,0x58b,0x5e3,0x63b,0x693,0x6eb,0x743,0x79b,0x7f3,0x84b,0x8a3,0x03f,0x097,0x0ef,0x147,0x19f,0x1f7,0x24f,0x2a7,0x2ff,0x357,0x3af,0x407 }, | ||||
| 	{ 0x764,0x7bc,0x814,0x86c,0x008,0x060,0x0b8,0x110,0x168,0x1c0,0x218,0x270,0x2c8,0x320,0x378,0x3d0,0x428,0x480,0x4d8,0x530,0x588,0x5e0,0x638,0x690,0x6e8,0x740,0x798,0x7f0,0x848,0x8a0,0x03c,0x094,0x0ec,0x144,0x19c,0x1f4,0x24c,0x2a4,0x2fc,0x354,0x3ac,0x404,0x45c }, | ||||
| 	{ 0x765,0x7bd,0x815,0x86d,0x009,0x061,0x0b9,0x111,0x169,0x1c1,0x219,0x271,0x2c9,0x321,0x379,0x3d1,0x429,0x481,0x4d9,0x531,0x589,0x5e1,0x639,0x691,0x6e9,0x741,0x799,0x7f1,0x849,0x8a1,0x03d,0x095,0x0ed,0x145,0x19d,0x1f5,0x24d,0x2a5,0x2fd,0x355,0x3ad,0x405,0x45d }, | ||||
| 	{ 0x7ba,0x812,0x86a,0x006,0x05e,0x0b6,0x10e,0x166,0x1be,0x216,0x26e,0x2c6,0x31e,0x376,0x3ce,0x426,0x47e,0x4d6,0x52e,0x586,0x5de,0x636,0x68e,0x6e6,0x73e,0x796,0x7ee,0x846,0x89e,0x03a,0x092,0x0ea,0x142,0x19a,0x1f2,0x24a,0x2a2,0x2fa,0x352,0x3aa,0x402,0x45a,0x4b2 }, | ||||
| 	{ 0x7bb,0x813,0x86b,0x007,0x05f,0x0b7,0x10f,0x167,0x1bf,0x217,0x26f,0x2c7,0x31f,0x377,0x3cf,0x427,0x47f,0x4d7,0x52f,0x587,0x5df,0x637,0x68f,0x6e7,0x73f,0x797,0x7ef,0x847,0x89f,0x03b,0x093,0x0eb,0x143,0x19b,0x1f3,0x24b,0x2a3,0x2fb,0x353,0x3ab,0x403,0x45b,0x4b3 }, | ||||
| 	{ 0x810,0x868,0x004,0x05c,0x0b4,0x10c,0x164,0x1bc,0x214,0x26c,0x2c4,0x31c,0x374,0x3cc,0x424,0x47c,0x4d4,0x52c,0x584,0x5dc,0x634,0x68c,0x6e4,0x73c,0x794,0x7ec,0x844,0x89c,0x038,0x090,0x0e8,0x140,0x198,0x1f0,0x248,0x2a0,0x2f8,0x350,0x3a8,0x400,0x458,0x4b0,0x508 }, | ||||
| 	{ 0x811,0x869,0x005,0x05d,0x0b5,0x10d,0x165,0x1bd,0x215,0x26d,0x2c5,0x31d,0x375,0x3cd,0x425,0x47d,0x4d5,0x52d,0x585,0x5dd,0x635,0x68d,0x6e5,0x73d,0x795,0x7ed,0x845,0x89d,0x039,0x091,0x0e9,0x141,0x199,0x1f1,0x249,0x2a1,0x2f9,0x351,0x3a9,0x401,0x459,0x4b1,0x509 }, | ||||
| 	{ 0x866,0x002,0x05a,0x0b2,0x10a,0x162,0x1ba,0x212,0x26a,0x2c2,0x31a,0x372,0x3ca,0x422,0x47a,0x4d2,0x52a,0x582,0x5da,0x632,0x68a,0x6e2,0x73a,0x792,0x7ea,0x842,0x89a,0x036,0x08e,0x0e6,0x13e,0x196,0x1ee,0x246,0x29e,0x2f6,0x34e,0x3a6,0x3fe,0x456,0x4ae,0x506,0x55e }, | ||||
| 	{ 0x867,0x003,0x05b,0x0b3,0x10b,0x163,0x1bb,0x213,0x26b,0x2c3,0x31b,0x373,0x3cb,0x423,0x47b,0x4d3,0x52b,0x583,0x5db,0x633,0x68b,0x6e3,0x73b,0x793,0x7eb,0x843,0x89b,0x037,0x08f,0x0e7,0x13f,0x197,0x1ef,0x247,0x29f,0x2f7,0x34f,0x3a7,0x3ff,0x457,0x4af,0x507,0x55f } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  ecc_source_byte - return data from the sector | ||||
|  *  at the given offset, masking anything | ||||
|  *  particular to a mode | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| static INLINE uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset) | ||||
| { | ||||
| 	/* in mode 2 always treat these as 0 bytes */ | ||||
| 	return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @fn  void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2) | ||||
|  * | ||||
|  * @brief   ------------------------------------------------- | ||||
|  *            ecc_compute_bytes - calculate an ECC value (P or Q) | ||||
|  *          -------------------------------------------------. | ||||
|  * | ||||
|  * @param   sector          The sector. | ||||
|  * @param   row             The row. | ||||
|  * @param   rowlen          The rowlen. | ||||
|  * @param [in,out]  val1    The first value. | ||||
|  * @param [in,out]  val2    The second value. | ||||
|  */ | ||||
| 
 | ||||
| void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t *val1, uint8_t *val2) | ||||
| { | ||||
|    int component; | ||||
| 	*val1 = *val2 = 0; | ||||
| 	for (component = 0; component < rowlen; component++) | ||||
| 	{ | ||||
| 		*val1 ^= ecc_source_byte(sector, row[component]); | ||||
| 		*val2 ^= ecc_source_byte(sector, row[component]); | ||||
| 		*val1 = ecclow[*val1]; | ||||
| 	} | ||||
| 	*val1 = ecchigh[ecclow[*val1] ^ *val2]; | ||||
| 	*val2 ^= *val1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @fn  int ecc_verify(const uint8_t *sector) | ||||
|  * | ||||
|  * @brief   ------------------------------------------------- | ||||
|  *            ecc_verify - verify the P and Q ECC codes in a sector | ||||
|  *          -------------------------------------------------. | ||||
|  * | ||||
|  * @param   sector  The sector. | ||||
|  * | ||||
|  * @return  true if it succeeds, false if it fails. | ||||
|  */ | ||||
| 
 | ||||
| int ecc_verify(const uint8_t *sector) | ||||
| { | ||||
|    int byte; | ||||
| 	/* first verify P bytes */ | ||||
| 	for (byte = 0; byte < ECC_P_NUM_BYTES; byte++) | ||||
| 	{ | ||||
| 		uint8_t val1, val2; | ||||
| 		ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &val1, &val2); | ||||
| 		if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2) | ||||
| 			return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* then verify Q bytes */ | ||||
| 	for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++) | ||||
| 	{ | ||||
| 		uint8_t val1, val2; | ||||
| 		ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &val1, &val2); | ||||
| 		if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2) | ||||
| 			return 0; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @fn  void ecc_generate(uint8_t *sector) | ||||
|  * | ||||
|  * @brief   ------------------------------------------------- | ||||
|  *            ecc_generate - generate the P and Q ECC codes for a sector, overwriting any | ||||
|  *            existing codes | ||||
|  *          -------------------------------------------------. | ||||
|  * | ||||
|  * @param [in,out]  sector  If non-null, the sector. | ||||
|  */ | ||||
| 
 | ||||
| void ecc_generate(uint8_t *sector) | ||||
| { | ||||
|    int byte; | ||||
| 	/* first verify P bytes */ | ||||
| 	for (byte = 0; byte < ECC_P_NUM_BYTES; byte++) | ||||
| 		ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, §or[ECC_P_OFFSET + byte], §or[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]); | ||||
| 
 | ||||
| 	/* then verify Q bytes */ | ||||
| 	for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++) | ||||
| 		ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, §or[ECC_Q_OFFSET + byte], §or[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @fn  void ecc_clear(uint8_t *sector) | ||||
|  * | ||||
|  * @brief   ------------------------------------------------- | ||||
|  *            ecc_clear - erase the ECC P and Q cods to 0 within a sector | ||||
|  *          -------------------------------------------------. | ||||
|  * | ||||
|  * @param [in,out]  sector  If non-null, the sector. | ||||
|  */ | ||||
| 
 | ||||
| void ecc_clear(uint8_t *sector) | ||||
| { | ||||
| 	memset(§or[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES); | ||||
| 	memset(§or[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES); | ||||
| } | ||||
| 
 | ||||
| #endif /* WANT_RAW_DATA_SECTOR */ | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,322 @@ | |||
| /* license:BSD-3-Clause
 | ||||
|  * copyright-holders:Aaron Giles | ||||
| *************************************************************************** | ||||
| 
 | ||||
|     flac.c | ||||
| 
 | ||||
|     FLAC compression wrappers | ||||
| 
 | ||||
| ***************************************************************************/ | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
| #include <libchdr/flac.h> | ||||
| #include <libchdr/minmax.h> | ||||
| 
 | ||||
| /***************************************************************************
 | ||||
|  *  FLAC DECODER | ||||
|  *************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| static FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); | ||||
| FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes); | ||||
| static void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); | ||||
| static FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); | ||||
| static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); | ||||
| FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void* client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); | ||||
| static void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  flac_decoder - constructor | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| void flac_decoder_init(flac_decoder *decoder) | ||||
| { | ||||
| 	decoder->decoder = FLAC__stream_decoder_new(); | ||||
| 	decoder->sample_rate = 0; | ||||
| 	decoder->channels = 0; | ||||
| 	decoder->bits_per_sample = 0; | ||||
| 	decoder->compressed_offset = 0; | ||||
| 	decoder->compressed_start = NULL; | ||||
| 	decoder->compressed_length = 0; | ||||
| 	decoder->compressed2_start = NULL; | ||||
| 	decoder->compressed2_length = 0; | ||||
| 	decoder->uncompressed_offset = 0; | ||||
| 	decoder->uncompressed_length = 0; | ||||
| 	decoder->uncompressed_swap = 0; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  flac_decoder - destructor | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| void flac_decoder_free(flac_decoder* decoder) | ||||
| { | ||||
| 	if ((decoder != NULL) && (decoder->decoder != NULL)) | ||||
| 		FLAC__stream_decoder_delete(decoder->decoder); | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  reset - reset state with the original | ||||
|  *  parameters | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| static int flac_decoder_internal_reset(flac_decoder* decoder) | ||||
| { | ||||
| 	decoder->compressed_offset = 0; | ||||
| 	if (FLAC__stream_decoder_init_stream(decoder->decoder, | ||||
| 				&flac_decoder_read_callback_static, | ||||
| 				NULL, | ||||
| 				&flac_decoder_tell_callback_static, | ||||
| 				NULL, | ||||
| 				NULL, | ||||
| 				&flac_decoder_write_callback_static, | ||||
| 				&flac_decoder_metadata_callback_static, | ||||
| 				&flac_decoder_error_callback_static, decoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK) | ||||
| 		return 0; | ||||
| 	return FLAC__stream_decoder_process_until_end_of_metadata(decoder->decoder); | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  reset - reset state with new memory parameters | ||||
|  *  and a custom-generated header | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length) | ||||
| { | ||||
| 	/* modify the template header with our parameters */ | ||||
| 	static const uint8_t s_header_template[0x2a] = | ||||
| 	{ | ||||
| 		0x66, 0x4C, 0x61, 0x43,                         /* +00: 'fLaC' stream header */ | ||||
| 		0x80,                                           /* +04: metadata block type 0 (STREAMINFO), */ | ||||
| 								/*      flagged as last block */ | ||||
| 		0x00, 0x00, 0x22,                               /* +05: metadata block length = 0x22 */ | ||||
| 		0x00, 0x00,                                     /* +08: minimum block size */ | ||||
| 		0x00, 0x00,                                     /* +0A: maximum block size */ | ||||
| 		0x00, 0x00, 0x00,                               /* +0C: minimum frame size (0 == unknown) */ | ||||
| 		0x00, 0x00, 0x00,                               /* +0F: maximum frame size (0 == unknown) */ | ||||
| 		0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, /* +12: sample rate (0x0ac44 == 44100), */ | ||||
| 								/*      numchannels (2), sample bits (16), */ | ||||
| 								/*      samples in stream (0 == unknown) */ | ||||
| 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* +1A: MD5 signature (0 == none) */ | ||||
| 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* +2A: start of stream data */ | ||||
| 	}; | ||||
| 	memcpy(decoder->custom_header, s_header_template, sizeof(s_header_template)); | ||||
| 	decoder->custom_header[0x08] = decoder->custom_header[0x0a] = block_size >> 8; | ||||
| 	decoder->custom_header[0x09] = decoder->custom_header[0x0b] = block_size & 0xff; | ||||
| 	decoder->custom_header[0x12] = sample_rate >> 12; | ||||
| 	decoder->custom_header[0x13] = sample_rate >> 4; | ||||
| 	decoder->custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1); | ||||
| 
 | ||||
| 	/* configure the header ahead of the provided buffer */ | ||||
| 	decoder->compressed_start = (const FLAC__byte *)(decoder->custom_header); | ||||
| 	decoder->compressed_length = sizeof(decoder->custom_header); | ||||
| 	decoder->compressed2_start = (const FLAC__byte *)(buffer); | ||||
| 	decoder->compressed2_length = length; | ||||
| 	return flac_decoder_internal_reset(decoder); | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  decode_interleaved - decode to an interleaved | ||||
|  *  sound stream | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian) | ||||
| { | ||||
| 	/* configure the uncompressed buffer */ | ||||
| 	memset(decoder->uncompressed_start, 0, sizeof(decoder->uncompressed_start)); | ||||
| 	decoder->uncompressed_start[0] = samples; | ||||
| 	decoder->uncompressed_offset = 0; | ||||
| 	decoder->uncompressed_length = num_samples; | ||||
| 	decoder->uncompressed_swap = swap_endian; | ||||
| 
 | ||||
| 	/* loop until we get everything we want */ | ||||
| 	while (decoder->uncompressed_offset < decoder->uncompressed_length) | ||||
| 		if (!FLAC__stream_decoder_process_single(decoder->decoder)) | ||||
| 			return 0; | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| #if 0 | ||||
| /*-------------------------------------------------
 | ||||
|  *  decode - decode to an multiple independent | ||||
|  *  data streams | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_endian) | ||||
| { | ||||
| 	/* make sure we don't have too many channels */ | ||||
| 	int chans = channels(); | ||||
| 	if (chans > ARRAY_LENGTH(m_uncompressed_start)) | ||||
| 		return false; | ||||
| 
 | ||||
| 	/* configure the uncompressed buffer */ | ||||
| 	memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start)); | ||||
| 	for (int curchan = 0; curchan < chans; curchan++) | ||||
| 		m_uncompressed_start[curchan] = samples[curchan]; | ||||
| 	m_uncompressed_offset = 0; | ||||
| 	m_uncompressed_length = num_samples; | ||||
| 	m_uncompressed_swap = swap_endian; | ||||
| 
 | ||||
| 	/* loop until we get everything we want */ | ||||
| 	while (m_uncompressed_offset < m_uncompressed_length) | ||||
| 		if (!FLAC__stream_decoder_process_single(m_decoder)) | ||||
| 			return false; | ||||
| 	return true; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  finish - finish up the decode | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| uint32_t flac_decoder_finish(flac_decoder* decoder) | ||||
| { | ||||
| 	/* get the final decoding position and move forward */ | ||||
| 	FLAC__uint64 position = 0; | ||||
| 	FLAC__stream_decoder_get_decode_position(decoder->decoder, &position); | ||||
| 	FLAC__stream_decoder_finish(decoder->decoder); | ||||
| 
 | ||||
| 	/* adjust position if we provided the header */ | ||||
| 	if (position == 0) | ||||
| 		return 0; | ||||
| 	if (decoder->compressed_start == (const FLAC__byte *)(decoder->custom_header)) | ||||
| 		position -= decoder->compressed_length; | ||||
| 	return position; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  read_callback - handle reads from the input | ||||
|  *  stream | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) | ||||
| { | ||||
| 	return flac_decoder_read_callback(client_data, buffer, bytes); | ||||
| } | ||||
| 
 | ||||
| FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes) | ||||
| { | ||||
| 	flac_decoder* decoder = (flac_decoder*)client_data; | ||||
| 
 | ||||
| 	uint32_t expected = *bytes; | ||||
| 
 | ||||
| 	/* copy from primary buffer first */ | ||||
| 	uint32_t outputpos = 0; | ||||
| 	if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length) | ||||
| 	{ | ||||
| 		uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed_length - decoder->compressed_offset); | ||||
| 		memcpy(&buffer[outputpos], decoder->compressed_start + decoder->compressed_offset, bytes_to_copy); | ||||
| 		outputpos += bytes_to_copy; | ||||
| 		decoder->compressed_offset += bytes_to_copy; | ||||
| 	} | ||||
| 
 | ||||
| 	/* once we're out of that, copy from the secondary buffer */ | ||||
| 	if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length + decoder->compressed2_length) | ||||
| 	{ | ||||
| 		uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed2_length - (decoder->compressed_offset - decoder->compressed_length)); | ||||
| 		memcpy(&buffer[outputpos], decoder->compressed2_start + decoder->compressed_offset - decoder->compressed_length, bytes_to_copy); | ||||
| 		outputpos += bytes_to_copy; | ||||
| 		decoder->compressed_offset += bytes_to_copy; | ||||
| 	} | ||||
| 	*bytes = outputpos; | ||||
| 
 | ||||
| 	/* return based on whether we ran out of data */ | ||||
| 	return (*bytes < expected) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  metadata_callback - handle STREAMINFO metadata | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) | ||||
| { | ||||
|    flac_decoder *fldecoder; | ||||
| 	/* ignore all but STREAMINFO metadata */ | ||||
| 	if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* parse out the data we care about */ | ||||
| 	fldecoder = (flac_decoder *)(client_data); | ||||
| 	fldecoder->sample_rate = metadata->data.stream_info.sample_rate; | ||||
| 	fldecoder->bits_per_sample = metadata->data.stream_info.bits_per_sample; | ||||
| 	fldecoder->channels = metadata->data.stream_info.channels; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  tell_callback - handle requests to find out | ||||
|  *  where in the input stream we are | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) | ||||
| { | ||||
| 	*absolute_byte_offset = ((flac_decoder *)client_data)->compressed_offset; | ||||
| 	return FLAC__STREAM_DECODER_TELL_STATUS_OK; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  write_callback - handle writes to the output | ||||
|  *  stream | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) | ||||
| { | ||||
| 	return flac_decoder_write_callback(client_data, frame, buffer); | ||||
| } | ||||
| 
 | ||||
| FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void *client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) | ||||
| { | ||||
| 	int sampnum, chan; | ||||
|    int shift, blocksize; | ||||
| 	flac_decoder * decoder = (flac_decoder *)client_data; | ||||
| 
 | ||||
| 	assert(frame->header.channels == decoder->channels); | ||||
| 
 | ||||
| 	/* interleaved case */ | ||||
| 	shift = decoder->uncompressed_swap ? 8 : 0; | ||||
| 	blocksize = frame->header.blocksize; | ||||
| 	if (decoder->uncompressed_start[1] == NULL) | ||||
| 	{ | ||||
| 		int16_t *dest = decoder->uncompressed_start[0] + decoder->uncompressed_offset * frame->header.channels; | ||||
| 		for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++) | ||||
| 			for (chan = 0; chan < frame->header.channels; chan++) | ||||
| 				*dest++ = (int16_t)((((uint16_t)buffer[chan][sampnum]) << shift) | (((uint16_t)buffer[chan][sampnum]) >> shift)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* non-interleaved case */ | ||||
| 	else | ||||
| 	{ | ||||
| 		for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++) | ||||
| 			for (chan = 0; chan < frame->header.channels; chan++) | ||||
| 				if (decoder->uncompressed_start[chan] != NULL) | ||||
| 					decoder->uncompressed_start[chan][decoder->uncompressed_offset] = (int16_t) ( (((uint16_t)(buffer[chan][sampnum])) << shift) | ( ((uint16_t)(buffer[chan][sampnum])) >> shift) ); | ||||
| 	} | ||||
| 	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @fn  void flac_decoder::error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) | ||||
|  * | ||||
|  * @brief   ------------------------------------------------- | ||||
|  *            error_callback - handle errors (ignore them) | ||||
|  *          -------------------------------------------------. | ||||
|  * | ||||
|  * @param   decoder             The decoder. | ||||
|  * @param   status              The status. | ||||
|  * @param [in,out]  client_data If non-null, information describing the client. | ||||
|  */ | ||||
| 
 | ||||
| void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) | ||||
| { | ||||
| } | ||||
|  | @ -0,0 +1,560 @@ | |||
| /* license:BSD-3-Clause
 | ||||
|  * copyright-holders:Aaron Giles | ||||
| **************************************************************************** | ||||
| 
 | ||||
|     huffman.c | ||||
| 
 | ||||
|     Static Huffman compression and decompression helpers. | ||||
| 
 | ||||
| **************************************************************************** | ||||
| 
 | ||||
|     Maximum codelength is officially (alphabetsize - 1). This would be 255 bits | ||||
|     (since we use 1 byte values). However, it is also dependent upon the number | ||||
|     of samples used, as follows: | ||||
| 
 | ||||
|          2 bits -> 3..4 samples | ||||
|          3 bits -> 5..7 samples | ||||
|          4 bits -> 8..12 samples | ||||
|          5 bits -> 13..20 samples | ||||
|          6 bits -> 21..33 samples | ||||
|          7 bits -> 34..54 samples | ||||
|          8 bits -> 55..88 samples | ||||
|          9 bits -> 89..143 samples | ||||
|         10 bits -> 144..232 samples | ||||
|         11 bits -> 233..376 samples | ||||
|         12 bits -> 377..609 samples | ||||
|         13 bits -> 610..986 samples | ||||
|         14 bits -> 987..1596 samples | ||||
|         15 bits -> 1597..2583 samples | ||||
|         16 bits -> 2584..4180 samples   -> note that a 4k data size guarantees codelength <= 16 bits | ||||
|         17 bits -> 4181..6764 samples | ||||
|         18 bits -> 6765..10945 samples | ||||
|         19 bits -> 10946..17710 samples | ||||
|         20 bits -> 17711..28656 samples | ||||
|         21 bits -> 28657..46367 samples | ||||
|         22 bits -> 46368..75024 samples | ||||
|         23 bits -> 75025..121392 samples | ||||
|         24 bits -> 121393..196417 samples | ||||
|         25 bits -> 196418..317810 samples | ||||
|         26 bits -> 317811..514228 samples | ||||
|         27 bits -> 514229..832039 samples | ||||
|         28 bits -> 832040..1346268 samples | ||||
|         29 bits -> 1346269..2178308 samples | ||||
|         30 bits -> 2178309..3524577 samples | ||||
|         31 bits -> 3524578..5702886 samples | ||||
|         32 bits -> 5702887..9227464 samples | ||||
| 
 | ||||
|     Looking at it differently, here is where powers of 2 fall into these buckets: | ||||
| 
 | ||||
|           256 samples -> 11 bits max | ||||
|           512 samples -> 12 bits max | ||||
|            1k samples -> 14 bits max | ||||
|            2k samples -> 15 bits max | ||||
|            4k samples -> 16 bits max | ||||
|            8k samples -> 18 bits max | ||||
|           16k samples -> 19 bits max | ||||
|           32k samples -> 21 bits max | ||||
|           64k samples -> 22 bits max | ||||
|          128k samples -> 24 bits max | ||||
|          256k samples -> 25 bits max | ||||
|          512k samples -> 27 bits max | ||||
|            1M samples -> 28 bits max | ||||
|            2M samples -> 29 bits max | ||||
|            4M samples -> 31 bits max | ||||
|            8M samples -> 32 bits max | ||||
| 
 | ||||
| **************************************************************************** | ||||
| 
 | ||||
|     Delta-RLE encoding works as follows: | ||||
| 
 | ||||
|     Starting value is assumed to be 0. All data is encoded as a delta | ||||
|     from the previous value, such that final[i] = final[i - 1] + delta. | ||||
|     Long runs of 0s are RLE-encoded as follows: | ||||
| 
 | ||||
|         0x100 = repeat count of 8 | ||||
|         0x101 = repeat count of 9 | ||||
|         0x102 = repeat count of 10 | ||||
|         0x103 = repeat count of 11 | ||||
|         0x104 = repeat count of 12 | ||||
|         0x105 = repeat count of 13 | ||||
|         0x106 = repeat count of 14 | ||||
|         0x107 = repeat count of 15 | ||||
|         0x108 = repeat count of 16 | ||||
|         0x109 = repeat count of 32 | ||||
|         0x10a = repeat count of 64 | ||||
|         0x10b = repeat count of 128 | ||||
|         0x10c = repeat count of 256 | ||||
|         0x10d = repeat count of 512 | ||||
|         0x10e = repeat count of 1024 | ||||
|         0x10f = repeat count of 2048 | ||||
| 
 | ||||
|     Note that repeat counts are reset at the end of a row, so if a 0 run | ||||
|     extends to the end of a row, a large repeat count may be used. | ||||
| 
 | ||||
|     The reason for starting the run counts at 8 is that 0 is expected to | ||||
|     be the most common symbol, and is typically encoded in 1 or 2 bits. | ||||
| 
 | ||||
| ***************************************************************************/ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <assert.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <libchdr/huffman.h> | ||||
| #include <libchdr/minmax.h> | ||||
| 
 | ||||
| /***************************************************************************
 | ||||
|  *  MACROS | ||||
|  *************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| #define MAKE_LOOKUP(code,bits)  (((code) << 5) | ((bits) & 0x1f)) | ||||
| 
 | ||||
| /***************************************************************************
 | ||||
|  *  IMPLEMENTATION | ||||
|  *************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  huffman_context_base - create an encoding/ | ||||
|  *  decoding context | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits) | ||||
| { | ||||
|    struct huffman_decoder* decoder; | ||||
| 	/* limit to 24 bits */ | ||||
| 	if (maxbits > 24) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	decoder = (struct huffman_decoder*)malloc(sizeof(struct huffman_decoder)); | ||||
| 	decoder->numcodes = numcodes; | ||||
| 	decoder->maxbits = maxbits; | ||||
| 	decoder->lookup = (lookup_value*)malloc(sizeof(lookup_value) * (1 << maxbits)); | ||||
| 	decoder->huffnode = (struct node_t*)malloc(sizeof(struct node_t) * numcodes); | ||||
| 	decoder->datahisto = NULL; | ||||
| 	decoder->prevdata = 0; | ||||
| 	decoder->rleremaining = 0; | ||||
| 	return decoder; | ||||
| } | ||||
| 
 | ||||
| void delete_huffman_decoder(struct huffman_decoder* decoder) | ||||
| { | ||||
| 	if (decoder != NULL) | ||||
| 	{ | ||||
| 		if (decoder->lookup != NULL) | ||||
| 			free(decoder->lookup); | ||||
| 		if (decoder->huffnode != NULL) | ||||
| 			free(decoder->huffnode); | ||||
| 		free(decoder); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  decode_one - decode a single code from the | ||||
|  *  huffman stream | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf) | ||||
| { | ||||
| 	/* peek ahead to get maxbits worth of data */ | ||||
| 	uint32_t bits = bitstream_peek(bitbuf, decoder->maxbits); | ||||
| 
 | ||||
| 	/* look it up, then remove the actual number of bits for this code */ | ||||
| 	lookup_value lookup = decoder->lookup[bits]; | ||||
| 	bitstream_remove(bitbuf, lookup & 0x1f); | ||||
| 
 | ||||
| 	/* return the value */ | ||||
| 	return lookup >> 5; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  import_tree_rle - import an RLE-encoded | ||||
|  *  huffman tree from a source data stream | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf) | ||||
| { | ||||
|    enum huffman_error error; | ||||
| 	/* bits per entry depends on the maxbits */ | ||||
| 	int numbits; | ||||
| 	int curnode; | ||||
| 	if (decoder->maxbits >= 16) | ||||
| 		numbits = 5; | ||||
| 	else if (decoder->maxbits >= 8) | ||||
| 		numbits = 4; | ||||
| 	else | ||||
| 		numbits = 3; | ||||
| 
 | ||||
| 	/* loop until we read all the nodes */ | ||||
| 	for (curnode = 0; curnode < decoder->numcodes; ) | ||||
| 	{ | ||||
| 		/* a non-one value is just raw */ | ||||
| 		int nodebits = bitstream_read(bitbuf, numbits); | ||||
| 		if (nodebits != 1) | ||||
| 			decoder->huffnode[curnode++].numbits = nodebits; | ||||
| 
 | ||||
| 		/* a one value is an escape code */ | ||||
| 		else | ||||
| 		{ | ||||
| 			/* a double 1 is just a single 1 */ | ||||
| 			nodebits = bitstream_read(bitbuf, numbits); | ||||
| 			if (nodebits == 1) | ||||
| 				decoder->huffnode[curnode++].numbits = nodebits; | ||||
| 
 | ||||
| 			/* otherwise, we need one for value for the repeat count */ | ||||
| 			else | ||||
| 			{ | ||||
| 				int repcount = bitstream_read(bitbuf, numbits) + 3; | ||||
| 				while (repcount--) | ||||
| 					decoder->huffnode[curnode++].numbits = nodebits; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* make sure we ended up with the right number */ | ||||
| 	if (curnode != decoder->numcodes) | ||||
| 		return HUFFERR_INVALID_DATA; | ||||
| 
 | ||||
| 	/* assign canonical codes for all nodes based on their code lengths */ | ||||
| 	error = huffman_assign_canonical_codes(decoder); | ||||
| 	if (error != HUFFERR_NONE) | ||||
| 		return error; | ||||
| 
 | ||||
| 	/* build the lookup table */ | ||||
| 	huffman_build_lookup_table(decoder); | ||||
| 
 | ||||
| 	/* determine final input length and report errors */ | ||||
| 	return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  import_tree_huffman - import a huffman-encoded | ||||
|  *  huffman tree from a source data stream | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf) | ||||
| { | ||||
| 	int last = 0; | ||||
| 	int curcode; | ||||
|    uint32_t temp; | ||||
|    enum huffman_error error; | ||||
| 	uint8_t rlefullbits = 0; | ||||
| 	int index, count = 0; | ||||
|    int start; | ||||
| 	/* start by parsing the lengths for the small tree */ | ||||
| 	struct huffman_decoder* smallhuff = create_huffman_decoder(24, 6); | ||||
| 
 | ||||
| 	smallhuff->huffnode[0].numbits = bitstream_read(bitbuf, 3); | ||||
| 	start = bitstream_read(bitbuf, 3) + 1; | ||||
| 
 | ||||
| 	for (index = 1; index < 24; index++) | ||||
| 	{ | ||||
| 		if (index < start || count == 7) | ||||
| 			smallhuff->huffnode[index].numbits = 0; | ||||
| 		else | ||||
| 		{ | ||||
| 			count = bitstream_read(bitbuf, 3); | ||||
| 			smallhuff->huffnode[index].numbits = (count == 7) ? 0 : count; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* then regenerate the tree */ | ||||
| 	error = huffman_assign_canonical_codes(smallhuff); | ||||
| 	if (error != HUFFERR_NONE) | ||||
| 		return error; | ||||
| 	huffman_build_lookup_table(smallhuff); | ||||
| 
 | ||||
| 	/* determine the maximum length of an RLE count */ | ||||
| 	temp = decoder->numcodes - 9; | ||||
| 	while (temp != 0) | ||||
| 		temp >>= 1, rlefullbits++; | ||||
| 
 | ||||
| 	/* now process the rest of the data */ | ||||
| 	for (curcode = 0; curcode < decoder->numcodes; ) | ||||
| 	{ | ||||
| 		int value = huffman_decode_one(smallhuff, bitbuf); | ||||
| 		if (value != 0) | ||||
| 			decoder->huffnode[curcode++].numbits = last = value - 1; | ||||
| 		else | ||||
| 		{ | ||||
| 			int count = bitstream_read(bitbuf, 3) + 2; | ||||
| 			if (count == 7+2) | ||||
| 				count += bitstream_read(bitbuf, rlefullbits); | ||||
| 			for ( ; count != 0 && curcode < decoder->numcodes; count--) | ||||
| 				decoder->huffnode[curcode++].numbits = last; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* make sure we ended up with the right number */ | ||||
| 	if (curcode != decoder->numcodes) | ||||
| 		return HUFFERR_INVALID_DATA; | ||||
| 
 | ||||
| 	/* assign canonical codes for all nodes based on their code lengths */ | ||||
| 	error = huffman_assign_canonical_codes(decoder); | ||||
| 	if (error != HUFFERR_NONE) | ||||
|    { | ||||
|       delete_huffman_decoder(smallhuff); | ||||
| 		return error; | ||||
|    } | ||||
| 
 | ||||
| 	/* build the lookup table */ | ||||
| 	huffman_build_lookup_table(decoder); | ||||
| 	delete_huffman_decoder(smallhuff); | ||||
| 
 | ||||
| 	/* determine final input length and report errors */ | ||||
| 	return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  compute_tree_from_histo - common backend for | ||||
|  *  computing a tree based on the data histogram | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder) | ||||
| { | ||||
| 	/* compute the number of data items in the histogram */ | ||||
| 	int i; | ||||
|    uint32_t upperweight; | ||||
| 	uint32_t lowerweight = 0; | ||||
| 	uint32_t sdatacount = 0; | ||||
| 	for (i = 0; i < decoder->numcodes; i++) | ||||
| 		sdatacount += decoder->datahisto[i]; | ||||
| 
 | ||||
| 	/* binary search to achieve the optimum encoding */ | ||||
| 	upperweight = sdatacount * 2; | ||||
| 	while (1) | ||||
| 	{ | ||||
| 		/* build a tree using the current weight */ | ||||
| 		uint32_t curweight = (upperweight + lowerweight) / 2; | ||||
| 		int curmaxbits = huffman_build_tree(decoder, sdatacount, curweight); | ||||
| 
 | ||||
| 		/* apply binary search here */ | ||||
| 		if (curmaxbits <= decoder->maxbits) | ||||
| 		{ | ||||
| 			lowerweight = curweight; | ||||
| 
 | ||||
| 			/* early out if it worked with the raw weights, or if we're done searching */ | ||||
| 			if (curweight == sdatacount || (upperweight - lowerweight) <= 1) | ||||
| 				break; | ||||
| 		} | ||||
| 		else | ||||
| 			upperweight = curweight; | ||||
| 	} | ||||
| 
 | ||||
| 	/* assign canonical codes for all nodes based on their code lengths */ | ||||
| 	return huffman_assign_canonical_codes(decoder); | ||||
| } | ||||
| 
 | ||||
| /***************************************************************************
 | ||||
|  *  INTERNAL FUNCTIONS | ||||
|  *************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  tree_node_compare - compare two tree nodes | ||||
|  *  by weight | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| static int huffman_tree_node_compare(const void *item1, const void *item2) | ||||
| { | ||||
| 	const struct node_t *node1 = *(const struct node_t **)item1; | ||||
| 	const struct node_t *node2 = *(const struct node_t **)item2; | ||||
| 	if (node2->weight != node1->weight) | ||||
| 		return node2->weight - node1->weight; | ||||
| 	if (node2->bits - node1->bits == 0) | ||||
| 		fprintf(stderr, "identical node sort keys, should not happen!\n"); | ||||
| 	return (int)node1->bits - (int)node2->bits; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  build_tree - build a huffman tree based on the | ||||
|  *  data distribution | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight) | ||||
| { | ||||
|    int nextalloc; | ||||
| 	int maxbits = 0; | ||||
| 	/* make a list of all non-zero nodes */ | ||||
| 	struct node_t** list   = (struct node_t**) | ||||
|       malloc(sizeof(struct node_t*) * decoder->numcodes * 2); | ||||
| 	int curcode, listitems = 0; | ||||
| 
 | ||||
| 	memset(decoder->huffnode, 0, | ||||
|          decoder->numcodes * sizeof(decoder->huffnode[0])); | ||||
| 
 | ||||
| 	for (curcode = 0; curcode < decoder->numcodes; curcode++) | ||||
| 		if (decoder->datahisto[curcode] != 0) | ||||
| 		{ | ||||
| 			list[listitems++] = &decoder->huffnode[curcode]; | ||||
| 			decoder->huffnode[curcode].count = decoder->datahisto[curcode]; | ||||
| 			decoder->huffnode[curcode].bits = curcode; | ||||
| 
 | ||||
| 			/* scale the weight by the current effective length, ensuring we don't go to 0 */ | ||||
| 			decoder->huffnode[curcode].weight = ((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata); | ||||
| 			if (decoder->huffnode[curcode].weight == 0) | ||||
| 				decoder->huffnode[curcode].weight = 1; | ||||
| 		} | ||||
| 
 | ||||
| #if 0 | ||||
|    { | ||||
|       unsigned i; | ||||
|       fprintf(stderr, "Pre-sort:\n"); | ||||
|       for (i = 0; i < listitems; i++) | ||||
|          fprintf(stderr, "weight: %d code: %d\n", | ||||
|                list[i]->m_weight, list[i]->m_bits); | ||||
|    } | ||||
| #endif | ||||
| 
 | ||||
| 	/* sort the list by weight, largest weight first */ | ||||
| 	qsort(&list[0], listitems, sizeof(list[0]), huffman_tree_node_compare); | ||||
| 
 | ||||
| #if 0 | ||||
|    fprintf(stderr, "Post-sort:\n"); | ||||
|    for (int i = 0; i < listitems; i++) { | ||||
|       fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits); | ||||
|    } | ||||
|    fprintf(stderr, "===================\n"); | ||||
| #endif | ||||
| 
 | ||||
| 	/* now build the tree */ | ||||
| 	nextalloc = decoder->numcodes; | ||||
| 
 | ||||
| 	while (listitems > 1) | ||||
| 	{ | ||||
| 		int curitem; | ||||
| 		/* remove lowest two items */ | ||||
| 		struct node_t* node1   = &(*list[--listitems]); | ||||
| 		struct node_t* node0   = &(*list[--listitems]); | ||||
| 
 | ||||
| 		/* create new node */ | ||||
| 		struct node_t* newnode = &decoder->huffnode[nextalloc++]; | ||||
| 		newnode->parent        = NULL; | ||||
| 		node0->parent          = | ||||
|          node1->parent       = newnode; | ||||
| 		newnode->weight        = | ||||
|          node0->weight + node1->weight; | ||||
| 
 | ||||
| 		/* insert into list at appropriate location */ | ||||
| 		for (curitem = 0; curitem < listitems; curitem++) | ||||
| 			if (newnode->weight > list[curitem]->weight) | ||||
| 			{ | ||||
| 				memmove(&list[curitem+1], | ||||
|                   &list[curitem], | ||||
|                   (listitems - curitem) * sizeof(list[0])); | ||||
| 				break; | ||||
| 			} | ||||
| 		list[curitem] = newnode; | ||||
| 		listitems++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* compute the number of bits in each code,
 | ||||
|     * and fill in another histogram */ | ||||
| 	for (curcode = 0; curcode < decoder->numcodes; curcode++) | ||||
| 	{ | ||||
| 		struct node_t *curnode; | ||||
| 		struct node_t* node = &decoder->huffnode[curcode]; | ||||
| 		node->numbits = 0; | ||||
| 		node->bits = 0; | ||||
| 
 | ||||
| 		/* if we have a non-zero weight, compute the number of bits */ | ||||
| 		if (node->weight > 0) | ||||
| 		{ | ||||
| 			/* determine the number of bits for this node */ | ||||
| 			for (curnode = node; | ||||
|                curnode->parent != NULL; curnode = curnode->parent) | ||||
| 				node->numbits++; | ||||
| 			if (node->numbits == 0) | ||||
| 				node->numbits = 1; | ||||
| 
 | ||||
| 			/* keep track of the max */ | ||||
| 			maxbits = MAX(maxbits, ((int)node->numbits)); | ||||
| 		} | ||||
| 	} | ||||
|    free(list); | ||||
| 	return maxbits; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  assign_canonical_codes - assign canonical codes | ||||
|  *  to all the nodes based on the number of bits | ||||
|  *  in each | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder) | ||||
| { | ||||
| 	uint32_t curstart = 0; | ||||
| 	/* build up a histogram of bit lengths */ | ||||
| 	int curcode, codelen; | ||||
| 	uint32_t bithisto[33] = { 0 }; | ||||
| 	for (curcode = 0; curcode < decoder->numcodes; curcode++) | ||||
| 	{ | ||||
| 		struct node_t* node = &decoder->huffnode[curcode]; | ||||
| 		if (node->numbits > decoder->maxbits) | ||||
| 			return HUFFERR_INTERNAL_INCONSISTENCY; | ||||
| 		if (node->numbits <= 32) | ||||
| 			bithisto[node->numbits]++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* for each code length, determine the starting code number */ | ||||
| 	for (codelen = 32; codelen > 0; codelen--) | ||||
| 	{ | ||||
| 		uint32_t nextstart = (curstart + bithisto[codelen]) >> 1; | ||||
| 		if (codelen != 1 && nextstart * 2 != (curstart + bithisto[codelen])) | ||||
| 			return HUFFERR_INTERNAL_INCONSISTENCY; | ||||
| 		bithisto[codelen] = curstart; | ||||
| 		curstart = nextstart; | ||||
| 	} | ||||
| 
 | ||||
| 	/* now assign canonical codes */ | ||||
| 	for (curcode = 0; curcode < decoder->numcodes; curcode++) | ||||
| 	{ | ||||
| 		struct node_t* node = &decoder->huffnode[curcode]; | ||||
| 		if (node->numbits > 0) | ||||
| 			node->bits = bithisto[node->numbits]++; | ||||
| 	} | ||||
| 	return HUFFERR_NONE; | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------
 | ||||
|  *  build_lookup_table - build a lookup table for | ||||
|  *  fast decoding | ||||
|  *------------------------------------------------- | ||||
|  */ | ||||
| 
 | ||||
| void huffman_build_lookup_table(struct huffman_decoder* decoder) | ||||
| { | ||||
| 	/* iterate over all codes */ | ||||
| 	int curcode; | ||||
| 	for (curcode = 0; curcode < decoder->numcodes; curcode++) | ||||
| 	{ | ||||
| 		/* process all nodes which have non-zero bits */ | ||||
| 		struct node_t* node = &decoder->huffnode[curcode]; | ||||
| 		if (node->numbits > 0) | ||||
| 		{ | ||||
|          int shift; | ||||
|          lookup_value *dest; | ||||
|          lookup_value *destend; | ||||
| 			/* set up the entry */ | ||||
| 			lookup_value value = MAKE_LOOKUP(curcode, node->numbits); | ||||
| 
 | ||||
| 			/* fill all matching entries */ | ||||
| 			shift = decoder->maxbits - node->numbits; | ||||
| 			dest = &decoder->lookup[node->bits << shift]; | ||||
| 			destend = &decoder->lookup[((node->bits + 1) << shift) - 1]; | ||||
| 			while (dest <= destend) | ||||
| 				*dest++ = value; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,391 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (rpng_encode.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <encodings/crc32.h> | ||||
| #include <streams/file_stream.h> | ||||
| #include <streams/trans_stream.h> | ||||
| 
 | ||||
| #include "rpng_internal.h" | ||||
| 
 | ||||
| #undef GOTO_END_ERROR | ||||
| #define GOTO_END_ERROR() do { \ | ||||
|    fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); \ | ||||
|    ret = false; \ | ||||
|    goto end; \ | ||||
| } while(0) | ||||
| 
 | ||||
| static void dword_write_be(uint8_t *buf, uint32_t val) | ||||
| { | ||||
|    *buf++ = (uint8_t)(val >> 24); | ||||
|    *buf++ = (uint8_t)(val >> 16); | ||||
|    *buf++ = (uint8_t)(val >>  8); | ||||
|    *buf++ = (uint8_t)(val >>  0); | ||||
| } | ||||
| 
 | ||||
| static bool png_write_crc(RFILE *file, const uint8_t *data, size_t size) | ||||
| { | ||||
|    uint8_t crc_raw[4] = {0}; | ||||
|    uint32_t crc       = encoding_crc32(0, data, size); | ||||
| 
 | ||||
|    dword_write_be(crc_raw, crc); | ||||
|    return filestream_write(file, crc_raw, sizeof(crc_raw)) == sizeof(crc_raw); | ||||
| } | ||||
| 
 | ||||
| static bool png_write_ihdr(RFILE *file, const struct png_ihdr *ihdr) | ||||
| { | ||||
|    uint8_t ihdr_raw[21]; | ||||
| 
 | ||||
|    ihdr_raw[0]  = '0';                 /* Size */ | ||||
|    ihdr_raw[1]  = '0'; | ||||
|    ihdr_raw[2]  = '0'; | ||||
|    ihdr_raw[3]  = '0'; | ||||
|    ihdr_raw[4]  = 'I'; | ||||
|    ihdr_raw[5]  = 'H'; | ||||
|    ihdr_raw[6]  = 'D'; | ||||
|    ihdr_raw[7]  = 'R'; | ||||
|    ihdr_raw[8]  =   0;                 /* Width */ | ||||
|    ihdr_raw[9]  =   0; | ||||
|    ihdr_raw[10] =   0; | ||||
|    ihdr_raw[11] =   0; | ||||
|    ihdr_raw[12] =   0;                 /* Height */ | ||||
|    ihdr_raw[13] =   0; | ||||
|    ihdr_raw[14] =   0; | ||||
|    ihdr_raw[15] =   0; | ||||
|    ihdr_raw[16] =   ihdr->depth;       /* Depth */ | ||||
|    ihdr_raw[17] =   ihdr->color_type; | ||||
|    ihdr_raw[18] =   ihdr->compression; | ||||
|    ihdr_raw[19] =   ihdr->filter; | ||||
|    ihdr_raw[20] =   ihdr->interlace; | ||||
| 
 | ||||
|    dword_write_be(ihdr_raw +  0, sizeof(ihdr_raw) - 8); | ||||
|    dword_write_be(ihdr_raw +  8, ihdr->width); | ||||
|    dword_write_be(ihdr_raw + 12, ihdr->height); | ||||
|    if (filestream_write(file, ihdr_raw, sizeof(ihdr_raw)) != sizeof(ihdr_raw)) | ||||
|       return false; | ||||
| 
 | ||||
|    if (!png_write_crc(file, ihdr_raw + sizeof(uint32_t), | ||||
|             sizeof(ihdr_raw) - sizeof(uint32_t))) | ||||
|       return false; | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static bool png_write_idat(RFILE *file, const uint8_t *data, size_t size) | ||||
| { | ||||
|    if (filestream_write(file, data, size) != (ssize_t)size) | ||||
|       return false; | ||||
| 
 | ||||
|    if (!png_write_crc(file, data + sizeof(uint32_t), size - sizeof(uint32_t))) | ||||
|       return false; | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static bool png_write_iend(RFILE *file) | ||||
| { | ||||
|    const uint8_t data[] = { | ||||
|       0, 0, 0, 0, | ||||
|       'I', 'E', 'N', 'D', | ||||
|    }; | ||||
| 
 | ||||
|    if (filestream_write(file, data, sizeof(data)) != sizeof(data)) | ||||
|       return false; | ||||
| 
 | ||||
|    if (!png_write_crc(file, data + sizeof(uint32_t), | ||||
|             sizeof(data) - sizeof(uint32_t))) | ||||
|       return false; | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static void copy_argb_line(uint8_t *dst, const uint32_t *src, unsigned width) | ||||
| { | ||||
|    unsigned i; | ||||
|    for (i = 0; i < width; i++) | ||||
|    { | ||||
|       uint32_t col = src[i]; | ||||
|       *dst++ = (uint8_t)(col >> 16); | ||||
|       *dst++ = (uint8_t)(col >>  8); | ||||
|       *dst++ = (uint8_t)(col >>  0); | ||||
|       *dst++ = (uint8_t)(col >> 24); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static void copy_bgr24_line(uint8_t *dst, const uint8_t *src, unsigned width) | ||||
| { | ||||
|    unsigned i; | ||||
|    for (i = 0; i < width; i++, dst += 3, src += 3) | ||||
|    { | ||||
|       dst[2] = src[0]; | ||||
|       dst[1] = src[1]; | ||||
|       dst[0] = src[2]; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static unsigned count_sad(const uint8_t *data, size_t size) | ||||
| { | ||||
|    size_t i; | ||||
|    unsigned cnt = 0; | ||||
|    for (i = 0; i < size; i++) | ||||
|       cnt += abs((int8_t)data[i]); | ||||
|    return cnt; | ||||
| } | ||||
| 
 | ||||
| static unsigned filter_up(uint8_t *target, const uint8_t *line, | ||||
|       const uint8_t *prev, unsigned width, unsigned bpp) | ||||
| { | ||||
|    unsigned i; | ||||
|    width *= bpp; | ||||
|    for (i = 0; i < width; i++) | ||||
|       target[i] = line[i] - prev[i]; | ||||
| 
 | ||||
|    return count_sad(target, width); | ||||
| } | ||||
| 
 | ||||
| static unsigned filter_sub(uint8_t *target, const uint8_t *line, | ||||
|       unsigned width, unsigned bpp) | ||||
| { | ||||
|    unsigned i; | ||||
|    width *= bpp; | ||||
|    for (i = 0; i < bpp; i++) | ||||
|       target[i] = line[i]; | ||||
|    for (i = bpp; i < width; i++) | ||||
|       target[i] = line[i] - line[i - bpp]; | ||||
| 
 | ||||
|    return count_sad(target, width); | ||||
| } | ||||
| 
 | ||||
| static unsigned filter_avg(uint8_t *target, const uint8_t *line, | ||||
|       const uint8_t *prev, unsigned width, unsigned bpp) | ||||
| { | ||||
|    unsigned i; | ||||
|    width *= bpp; | ||||
|    for (i = 0; i < bpp; i++) | ||||
|       target[i] = line[i] - (prev[i] >> 1); | ||||
|    for (i = bpp; i < width; i++) | ||||
|       target[i] = line[i] - ((line[i - bpp] + prev[i]) >> 1); | ||||
| 
 | ||||
|    return count_sad(target, width); | ||||
| } | ||||
| 
 | ||||
| static unsigned filter_paeth(uint8_t *target, | ||||
|       const uint8_t *line, const uint8_t *prev, | ||||
|       unsigned width, unsigned bpp) | ||||
| { | ||||
|    unsigned i; | ||||
|    width *= bpp; | ||||
|    for (i = 0; i < bpp; i++) | ||||
|       target[i] = line[i] - paeth(0, prev[i], 0); | ||||
|    for (i = bpp; i < width; i++) | ||||
|       target[i] = line[i] - paeth(line[i - bpp], prev[i], prev[i - bpp]); | ||||
| 
 | ||||
|    return count_sad(target, width); | ||||
| } | ||||
| 
 | ||||
| static bool rpng_save_image(const char *path, | ||||
|       const uint8_t *data, | ||||
|       unsigned width, unsigned height, unsigned pitch, unsigned bpp) | ||||
| { | ||||
|    unsigned h; | ||||
|    bool ret = true; | ||||
|    struct png_ihdr ihdr = {0}; | ||||
| 
 | ||||
|    const struct trans_stream_backend *stream_backend = NULL; | ||||
|    size_t encode_buf_size  = 0; | ||||
|    uint8_t *encode_buf     = NULL; | ||||
|    uint8_t *deflate_buf    = NULL; | ||||
|    uint8_t *rgba_line      = NULL; | ||||
|    uint8_t *up_filtered    = NULL; | ||||
|    uint8_t *sub_filtered   = NULL; | ||||
|    uint8_t *avg_filtered   = NULL; | ||||
|    uint8_t *paeth_filtered = NULL; | ||||
|    uint8_t *prev_encoded   = NULL; | ||||
|    uint8_t *encode_target  = NULL; | ||||
|    void *stream            = NULL; | ||||
|    uint32_t total_in       = 0; | ||||
|    uint32_t total_out      = 0; | ||||
|    RFILE *file             = filestream_open(path, | ||||
|          RETRO_VFS_FILE_ACCESS_WRITE, | ||||
|          RETRO_VFS_FILE_ACCESS_HINT_NONE); | ||||
|    if (!file) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    stream_backend = trans_stream_get_zlib_deflate_backend(); | ||||
| 
 | ||||
|    if (filestream_write(file, png_magic, sizeof(png_magic)) != sizeof(png_magic)) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    ihdr.width = width; | ||||
|    ihdr.height = height; | ||||
|    ihdr.depth = 8; | ||||
|    ihdr.color_type = bpp == sizeof(uint32_t) ? 6 : 2; /* RGBA or RGB */ | ||||
|    if (!png_write_ihdr(file, &ihdr)) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    encode_buf_size = (width * bpp + 1) * height; | ||||
|    encode_buf = (uint8_t*)malloc(encode_buf_size); | ||||
|    if (!encode_buf) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    prev_encoded = (uint8_t*)calloc(1, width * bpp); | ||||
|    if (!prev_encoded) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    rgba_line      = (uint8_t*)malloc(width * bpp); | ||||
|    up_filtered    = (uint8_t*)malloc(width * bpp); | ||||
|    sub_filtered   = (uint8_t*)malloc(width * bpp); | ||||
|    avg_filtered   = (uint8_t*)malloc(width * bpp); | ||||
|    paeth_filtered = (uint8_t*)malloc(width * bpp); | ||||
|    if (!rgba_line || !up_filtered || !sub_filtered || !avg_filtered || !paeth_filtered) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    encode_target = encode_buf; | ||||
|    for (h = 0; h < height; | ||||
|          h++, encode_target += width * bpp, data += pitch) | ||||
|    { | ||||
|       if (bpp == sizeof(uint32_t)) | ||||
|          copy_argb_line(rgba_line, (const uint32_t*)data, width); | ||||
|       else | ||||
|          copy_bgr24_line(rgba_line, data, width); | ||||
| 
 | ||||
|       /* Try every filtering method, and choose the method
 | ||||
|        * which has most entries as zero. | ||||
|        * | ||||
|        * This is probably not very optimal, but it's very | ||||
|        * simple to implement. | ||||
|        */ | ||||
|       { | ||||
|          unsigned none_score  = count_sad(rgba_line, width * bpp); | ||||
|          unsigned up_score    = filter_up(up_filtered, rgba_line, prev_encoded, width, bpp); | ||||
|          unsigned sub_score   = filter_sub(sub_filtered, rgba_line, width, bpp); | ||||
|          unsigned avg_score   = filter_avg(avg_filtered, rgba_line, prev_encoded, width, bpp); | ||||
|          unsigned paeth_score = filter_paeth(paeth_filtered, rgba_line, prev_encoded, width, bpp); | ||||
| 
 | ||||
|          uint8_t filter       = 0; | ||||
|          unsigned min_sad     = none_score; | ||||
|          const uint8_t *chosen_filtered = rgba_line; | ||||
| 
 | ||||
|          if (sub_score < min_sad) | ||||
|          { | ||||
|             filter = 1; | ||||
|             chosen_filtered = sub_filtered; | ||||
|             min_sad = sub_score; | ||||
|          } | ||||
| 
 | ||||
|          if (up_score < min_sad) | ||||
|          { | ||||
|             filter = 2; | ||||
|             chosen_filtered = up_filtered; | ||||
|             min_sad = up_score; | ||||
|          } | ||||
| 
 | ||||
|          if (avg_score < min_sad) | ||||
|          { | ||||
|             filter = 3; | ||||
|             chosen_filtered = avg_filtered; | ||||
|             min_sad = avg_score; | ||||
|          } | ||||
| 
 | ||||
|          if (paeth_score < min_sad) | ||||
|          { | ||||
|             filter = 4; | ||||
|             chosen_filtered = paeth_filtered; | ||||
|          } | ||||
| 
 | ||||
|          *encode_target++ = filter; | ||||
|          memcpy(encode_target, chosen_filtered, width * bpp); | ||||
| 
 | ||||
|          memcpy(prev_encoded, rgba_line, width * bpp); | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    deflate_buf = (uint8_t*)malloc(encode_buf_size * 2); /* Just to be sure. */ | ||||
|    if (!deflate_buf) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    stream = stream_backend->stream_new(); | ||||
| 
 | ||||
|    if (!stream) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    stream_backend->set_in( | ||||
|          stream, | ||||
|          encode_buf, | ||||
|          (unsigned)encode_buf_size); | ||||
|    stream_backend->set_out( | ||||
|          stream, | ||||
|          deflate_buf + 8, | ||||
|          (unsigned)(encode_buf_size * 2)); | ||||
| 
 | ||||
|    if (!stream_backend->trans(stream, true, &total_in, &total_out, NULL)) | ||||
|    { | ||||
|       GOTO_END_ERROR(); | ||||
|    } | ||||
| 
 | ||||
|    memcpy(deflate_buf + 4, "IDAT", 4); | ||||
|    dword_write_be(deflate_buf + 0,        ((uint32_t)total_out)); | ||||
|    if (!png_write_idat(file, deflate_buf, ((size_t)total_out + 8))) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
|    if (!png_write_iend(file)) | ||||
|       GOTO_END_ERROR(); | ||||
| 
 | ||||
| end: | ||||
|    if (file) | ||||
|       filestream_close(file); | ||||
|    free(encode_buf); | ||||
|    free(deflate_buf); | ||||
|    free(rgba_line); | ||||
|    free(prev_encoded); | ||||
|    free(up_filtered); | ||||
|    free(sub_filtered); | ||||
|    free(avg_filtered); | ||||
|    free(paeth_filtered); | ||||
| 
 | ||||
|    if (stream_backend) | ||||
|    { | ||||
|       if (stream) | ||||
|       { | ||||
|          if (stream_backend->stream_free) | ||||
|             stream_backend->stream_free(stream); | ||||
|       } | ||||
|    } | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| bool rpng_save_image_argb(const char *path, const uint32_t *data, | ||||
|       unsigned width, unsigned height, unsigned pitch) | ||||
| { | ||||
|    return rpng_save_image(path, (const uint8_t*)data, | ||||
|          width, height, pitch, sizeof(uint32_t)); | ||||
| } | ||||
| 
 | ||||
| bool rpng_save_image_bgr24(const char *path, const uint8_t *data, | ||||
|       unsigned width, unsigned height, unsigned pitch) | ||||
| { | ||||
|    return rpng_save_image(path, (const uint8_t*)data, | ||||
|          width, height, pitch, 3); | ||||
| } | ||||
|  | @ -0,0 +1,56 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (rpng_internal.h). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _RPNG_COMMON_H | ||||
| #define _RPNG_COMMON_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <filters.h> | ||||
| #include <formats/rpng.h> | ||||
| 
 | ||||
| #undef GOTO_END_ERROR | ||||
| #define GOTO_END_ERROR() do { \ | ||||
|    fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); \ | ||||
|    ret = false; \ | ||||
|    goto end; \ | ||||
| } while(0) | ||||
| 
 | ||||
| #ifndef ARRAY_SIZE | ||||
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) | ||||
| #endif | ||||
| 
 | ||||
| static const uint8_t png_magic[8] = { | ||||
|    0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a, | ||||
| }; | ||||
| 
 | ||||
| struct png_ihdr | ||||
| { | ||||
|    uint32_t width; | ||||
|    uint32_t height; | ||||
|    uint8_t depth; | ||||
|    uint8_t color_type; | ||||
|    uint8_t compression; | ||||
|    uint8_t filter; | ||||
|    uint8_t interlace; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,446 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (rtga.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| /* Modified version of stb_image's TGA sources. */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <stdarg.h> | ||||
| #include <stddef.h> /* ptrdiff_t on osx */ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_assert.h> | ||||
| #include <retro_inline.h> | ||||
| 
 | ||||
| #include <formats/image.h> | ||||
| #include <formats/rtga.h> | ||||
| 
 | ||||
| #define RTGA_COMPUTE_Y(r, g, b) ((uint8_t)((((r) * 77) + ((g) * 150) +  (29 * (b))) >> 8)) | ||||
| 
 | ||||
| 
 | ||||
| struct rtga | ||||
| { | ||||
|    uint8_t *buff_data; | ||||
|    uint32_t *output_image; | ||||
| }; | ||||
| 
 | ||||
| typedef struct | ||||
| { | ||||
|    uint32_t img_x, img_y; | ||||
|    int img_n, img_out_n; | ||||
| 
 | ||||
|    int buflen; | ||||
|    uint8_t buffer_start[128]; | ||||
| 
 | ||||
|    uint8_t *img_buffer; | ||||
|    uint8_t *img_buffer_end; | ||||
|    uint8_t *img_buffer_original; | ||||
| } rtga__context; | ||||
| 
 | ||||
| static INLINE uint8_t rtga__get8(rtga__context *s) | ||||
| { | ||||
|    if (s->img_buffer < s->img_buffer_end) | ||||
|       return *s->img_buffer++; | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| static void rtga__skip(rtga__context *s, int n) | ||||
| { | ||||
|    if (n < 0) | ||||
|    { | ||||
|       s->img_buffer = s->img_buffer_end; | ||||
|       return; | ||||
|    } | ||||
|    s->img_buffer += n; | ||||
| } | ||||
| 
 | ||||
| static int rtga__getn(rtga__context *s, uint8_t *buffer, int n) | ||||
| { | ||||
|    if (s->img_buffer+n <= s->img_buffer_end) | ||||
|    { | ||||
|       memcpy(buffer, s->img_buffer, n); | ||||
|       s->img_buffer += n; | ||||
|       return 1; | ||||
|    } | ||||
| 
 | ||||
|    return 0; | ||||
| } | ||||
| 
 | ||||
| static int rtga__get16le(rtga__context *s) | ||||
| { | ||||
|    int z = rtga__get8(s); | ||||
|    return z + (rtga__get8(s) << 8); | ||||
| } | ||||
| 
 | ||||
| static unsigned char *rtga__convert_format( | ||||
|       unsigned char *data, | ||||
|       int img_n, | ||||
|       int req_comp, | ||||
|       unsigned int x, | ||||
|       unsigned int y) | ||||
| { | ||||
|    int i,j; | ||||
|    unsigned char *good = (unsigned char *) malloc(req_comp * x * y); | ||||
| 
 | ||||
|    if (!good) | ||||
|    { | ||||
|       free(data); | ||||
|       return NULL; | ||||
|    } | ||||
| 
 | ||||
|    for (j=0; j < (int) y; ++j) | ||||
|    { | ||||
|       unsigned char *src  = data + j * x * img_n   ; | ||||
|       unsigned char *dest = good + j * x * req_comp; | ||||
| 
 | ||||
|       switch (((img_n)*8+(req_comp))) | ||||
|       { | ||||
|          case ((1)*8+(2)): | ||||
|             for(i=x-1; i >= 0; --i, src += 1, dest += 2) | ||||
|                dest[0]=src[0], dest[1]=255; | ||||
|             break; | ||||
|          case ((1)*8+(3)): | ||||
|             for(i=x-1; i >= 0; --i, src += 1, dest += 3) | ||||
|                dest[0]=dest[1]=dest[2]=src[0]; | ||||
|             break; | ||||
|          case ((1)*8+(4)): | ||||
|             for(i=x-1; i >= 0; --i, src += 1, dest += 4) | ||||
|                dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; | ||||
|             break; | ||||
|          case ((2)*8+(1)): | ||||
|             for(i=x-1; i >= 0; --i, src += 2, dest += 1) | ||||
|                dest[0]=src[0]; | ||||
|             break; | ||||
|          case ((2)*8+(3)): | ||||
|             for(i=x-1; i >= 0; --i, src += 2, dest += 3) | ||||
|                dest[0]=dest[1]=dest[2]=src[0]; | ||||
|             break; | ||||
|          case ((2)*8+(4)): | ||||
|             for(i=x-1; i >= 0; --i, src += 2, dest += 4) | ||||
|                dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; | ||||
|             break; | ||||
|          case ((3)*8+(4)): | ||||
|             for(i=x-1; i >= 0; --i, src += 3, dest += 4) | ||||
|                dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; | ||||
|             break; | ||||
|          case ((3)*8+(1)): | ||||
|             for(i=x-1; i >= 0; --i, src += 3, dest += 1) | ||||
|                dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]); | ||||
|             break; | ||||
|          case ((3)*8+(2)): | ||||
|             for(i=x-1; i >= 0; --i, src += 3, dest += 2) | ||||
|                dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = 255; | ||||
|             break; | ||||
|          case ((4)*8+(1)): | ||||
|             for(i=x-1; i >= 0; --i, src += 4, dest += 1) | ||||
|                dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]); | ||||
|             break; | ||||
|          case ((4)*8+(2)): | ||||
|             for(i=x-1; i >= 0; --i, src += 4, dest += 2) | ||||
|                dest[0] = RTGA_COMPUTE_Y(src[0],src[1],src[2]), dest[1] = src[3]; | ||||
|             break; | ||||
|          case ((4)*8+(3)): | ||||
|             for(i=x-1; i >= 0; --i, src += 4, dest += 3) | ||||
|                dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; | ||||
|             break; | ||||
|          default: | ||||
|             break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    free(data); | ||||
|    return good; | ||||
| } | ||||
| 
 | ||||
| static uint8_t *rtga__tga_load(rtga__context *s, | ||||
|       unsigned *x, unsigned *y, int *comp, int req_comp) | ||||
| { | ||||
|    /* Read in the TGA header stuff */ | ||||
|    int tga_offset          = rtga__get8(s); | ||||
|    int tga_indexed         = rtga__get8(s); | ||||
|    int tga_image_type      = rtga__get8(s); | ||||
|    int tga_is_RLE          = 0; | ||||
|    int tga_palette_start   = rtga__get16le(s); | ||||
|    int tga_palette_len     = rtga__get16le(s); | ||||
|    int tga_palette_bits    = rtga__get8(s); | ||||
|    int tga_x_origin        = rtga__get16le(s); | ||||
|    int tga_y_origin        = rtga__get16le(s); | ||||
|    int tga_width           = rtga__get16le(s); | ||||
|    int tga_height          = rtga__get16le(s); | ||||
|    int tga_bits_per_pixel  = rtga__get8(s); | ||||
|    int tga_comp            = tga_bits_per_pixel / 8; | ||||
|    int tga_inverted        = rtga__get8(s); | ||||
| 
 | ||||
|    /*   image data */ | ||||
|    unsigned char *tga_data = NULL; | ||||
| 
 | ||||
|    (void)tga_palette_start; | ||||
|    (void)tga_x_origin; | ||||
|    (void)tga_y_origin; | ||||
| 
 | ||||
|    /*   do a tiny bit of precessing */ | ||||
|    if ( tga_image_type >= 8 ) | ||||
|    { | ||||
|       tga_image_type -= 8; | ||||
|       tga_is_RLE = 1; | ||||
|    } | ||||
| 
 | ||||
|    /* int tga_alpha_bits = tga_inverted & 15; */ | ||||
|    tga_inverted = 1 - ((tga_inverted >> 5) & 1); | ||||
| 
 | ||||
|    /*   error check */ | ||||
|    if ( | ||||
|          (tga_width < 1) || (tga_height < 1) || | ||||
|          (tga_image_type < 1) || (tga_image_type > 3) || | ||||
|          ( | ||||
|           (tga_bits_per_pixel != 8)  && (tga_bits_per_pixel != 16) && | ||||
|           (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32) | ||||
|          ) | ||||
|       ) | ||||
|       return NULL; /* we don't report this as a bad TGA because we don't even know if it's TGA */ | ||||
| 
 | ||||
|    /*   If paletted, then we will use the number of bits from the palette */ | ||||
|    if ( tga_indexed ) | ||||
|       tga_comp = tga_palette_bits / 8; | ||||
| 
 | ||||
|    /*   TGA info */ | ||||
|    *x = tga_width; | ||||
|    *y = tga_height; | ||||
|    if (comp) *comp = tga_comp; | ||||
| 
 | ||||
|    tga_data = (unsigned char*)malloc( (size_t)tga_width * tga_height * tga_comp ); | ||||
|    if (!tga_data) | ||||
|       return NULL; | ||||
| 
 | ||||
|    /* skip to the data's starting position (offset usually = 0) */ | ||||
|    rtga__skip(s, tga_offset ); | ||||
| 
 | ||||
|    if ( !tga_indexed && !tga_is_RLE) | ||||
|    { | ||||
|       int i; | ||||
|       for (i=0; i < tga_height; ++i) | ||||
|       { | ||||
|          int y = tga_inverted ? tga_height -i - 1 : i; | ||||
|          uint8_t *tga_row = tga_data + y*tga_width*tga_comp; | ||||
|          rtga__getn(s, tga_row, tga_width * tga_comp); | ||||
|       } | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       int i, j; | ||||
|       int RLE_repeating          = 0; | ||||
|       int RLE_count              = 0; | ||||
|       int read_next_pixel        = 1; | ||||
|       unsigned char raw_data[4]  = {0}; | ||||
|       unsigned char *tga_palette = NULL; | ||||
| 
 | ||||
|       /*   Do I need to load a palette? */ | ||||
|       if ( tga_indexed) | ||||
|       { | ||||
|          /*   any data to skip? (offset usually = 0) */ | ||||
|          rtga__skip(s, tga_palette_start ); | ||||
|          /*   load the palette */ | ||||
|          tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); | ||||
| 
 | ||||
|          if (!tga_palette) | ||||
|          { | ||||
|             free(tga_data); | ||||
|             return NULL; | ||||
|          } | ||||
| 
 | ||||
|          if (!rtga__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) | ||||
|          { | ||||
|             free(tga_data); | ||||
|             free(tga_palette); | ||||
|             return NULL; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       /*   load the data */ | ||||
|       for (i=0; i < tga_width * tga_height; ++i) | ||||
|       { | ||||
|          /*   if I'm in RLE mode, do I need to get a RLE rtga__pngchunk? */ | ||||
|          if ( tga_is_RLE ) | ||||
|          { | ||||
|             if ( RLE_count == 0 ) | ||||
|             { | ||||
|                /*   yep, get the next byte as a RLE command */ | ||||
|                int RLE_cmd     = rtga__get8(s); | ||||
|                RLE_count       = 1 + (RLE_cmd & 127); | ||||
|                RLE_repeating   = RLE_cmd >> 7; | ||||
|                read_next_pixel = 1; | ||||
|             } | ||||
|             else if ( !RLE_repeating ) | ||||
|                read_next_pixel = 1; | ||||
|          } | ||||
|          else | ||||
|             read_next_pixel = 1; | ||||
| 
 | ||||
|          /*   OK, if I need to read a pixel, do it now */ | ||||
|          if ( read_next_pixel ) | ||||
|          { | ||||
|             /*   load however much data we did have */ | ||||
|             if ( tga_indexed ) | ||||
|             { | ||||
|                /*   read in 1 byte, then perform the lookup */ | ||||
|                int pal_idx = rtga__get8(s); | ||||
|                if ( pal_idx >= tga_palette_len ) /* invalid index */ | ||||
|                   pal_idx = 0; | ||||
|                pal_idx *= tga_bits_per_pixel / 8; | ||||
|                for (j = 0; j*8 < tga_bits_per_pixel; ++j) | ||||
|                   raw_data[j] = tga_palette[pal_idx+j]; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                /* read in the data raw */ | ||||
|                for (j = 0; j*8 < tga_bits_per_pixel; ++j) | ||||
|                   raw_data[j] = rtga__get8(s); | ||||
|             } | ||||
| 
 | ||||
|             /*   clear the reading flag for the next pixel */ | ||||
|             read_next_pixel = 0; | ||||
|          } /* end of reading a pixel */ | ||||
| 
 | ||||
|          /* copy data */ | ||||
|          for (j = 0; j < tga_comp; ++j) | ||||
|             tga_data[i*tga_comp+j] = raw_data[j]; | ||||
| 
 | ||||
|          /*   in case we're in RLE mode, keep counting down */ | ||||
|          --RLE_count; | ||||
|       } | ||||
| 
 | ||||
|       /*   do I need to invert the image? */ | ||||
|       if ( tga_inverted ) | ||||
|       { | ||||
|          for (j = 0; j*2 < tga_height; ++j) | ||||
|          { | ||||
|             int index1 = j * tga_width * tga_comp; | ||||
|             int index2 = (tga_height - 1 - j) * tga_width * tga_comp; | ||||
| 
 | ||||
|             for (i = tga_width * tga_comp; i > 0; --i) | ||||
|             { | ||||
|                unsigned char temp = tga_data[index1]; | ||||
|                tga_data[index1] = tga_data[index2]; | ||||
|                tga_data[index2] = temp; | ||||
|                ++index1; | ||||
|                ++index2; | ||||
|             } | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       /* Clear my palette, if I had one */ | ||||
|       if ( tga_palette != NULL ) | ||||
|          free( tga_palette ); | ||||
|    } | ||||
| 
 | ||||
|    /* swap RGB */ | ||||
|    if (tga_comp >= 3) | ||||
|    { | ||||
|       int i; | ||||
|       unsigned char* tga_pixel = tga_data; | ||||
| 
 | ||||
|       for (i = 0; i < tga_width * tga_height; ++i) | ||||
|       { | ||||
|          unsigned char temp  = tga_pixel[0]; | ||||
|          tga_pixel[0]        = tga_pixel[2]; | ||||
|          tga_pixel[2]        = temp; | ||||
|          tga_pixel          += tga_comp; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    /* convert to target component count */ | ||||
|    if (     (req_comp) | ||||
|          && (req_comp >= 1 && req_comp <= 4) | ||||
|          && (req_comp != tga_comp)) | ||||
|    { | ||||
|       tga_data = rtga__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); | ||||
|    } | ||||
| 
 | ||||
|    return tga_data; | ||||
| } | ||||
| 
 | ||||
| static uint8_t *rtga_load_from_memory(uint8_t const *buffer, int len, | ||||
|       unsigned *x, unsigned *y, int *comp, int req_comp) | ||||
| { | ||||
|    rtga__context s; | ||||
| 
 | ||||
|    s.img_buffer          = (uint8_t *)buffer; | ||||
|    s.img_buffer_original = (uint8_t *) buffer; | ||||
|    s.img_buffer_end      = (uint8_t *) buffer+len; | ||||
| 
 | ||||
|    return rtga__tga_load(&s,x,y,comp,req_comp); | ||||
| } | ||||
| 
 | ||||
| int rtga_process_image(rtga_t *rtga, void **buf_data, | ||||
|       size_t size, unsigned *width, unsigned *height) | ||||
| { | ||||
|    int comp; | ||||
|    unsigned size_tex     = 0; | ||||
| 
 | ||||
|    if (!rtga) | ||||
|       return IMAGE_PROCESS_ERROR; | ||||
| 
 | ||||
|    rtga->output_image   = (uint32_t*)rtga_load_from_memory(rtga->buff_data, | ||||
|                            (int)size, width, height, &comp, 4); | ||||
|    *buf_data             = rtga->output_image; | ||||
|    size_tex              = (*width) * (*height); | ||||
| 
 | ||||
|    /* Convert RGBA to ARGB */ | ||||
|    while(size_tex--) | ||||
|    { | ||||
|       unsigned int texel = rtga->output_image[size_tex]; | ||||
|       unsigned int A     = texel & 0xFF000000; | ||||
|       unsigned int B     = texel & 0x00FF0000; | ||||
|       unsigned int G     = texel & 0x0000FF00; | ||||
|       unsigned int R     = texel & 0x000000FF; | ||||
|       ((unsigned int*)rtga->output_image)[size_tex] = A | (R << 16) | G | (B >> 16); | ||||
|    }; | ||||
| 
 | ||||
|    return IMAGE_PROCESS_END; | ||||
| } | ||||
| 
 | ||||
| bool rtga_set_buf_ptr(rtga_t *rtga, void *data) | ||||
| { | ||||
|    if (!rtga) | ||||
|       return false; | ||||
| 
 | ||||
|    rtga->buff_data = (uint8_t*)data; | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| void rtga_free(rtga_t *rtga) | ||||
| { | ||||
|    if (!rtga) | ||||
|       return; | ||||
| 
 | ||||
|    free(rtga); | ||||
| } | ||||
| 
 | ||||
| rtga_t *rtga_alloc(void) | ||||
| { | ||||
|    rtga_t *rtga = (rtga_t*)calloc(1, sizeof(*rtga)); | ||||
|    if (!rtga) | ||||
|       return NULL; | ||||
|    return rtga; | ||||
| } | ||||
|  | @ -0,0 +1,186 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (rwav.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <stdarg.h> | ||||
| #include <stddef.h> /* ptrdiff_t on osx */ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <formats/rwav.h> | ||||
| 
 | ||||
| enum | ||||
| { | ||||
|    ITER_BEGIN, | ||||
|    ITER_COPY_SAMPLES, | ||||
|    ITER_COPY_SAMPLES_8, | ||||
|    ITER_COPY_SAMPLES_16 | ||||
| }; | ||||
| 
 | ||||
| struct rwav_iterator | ||||
| { | ||||
|    rwav_t *out; | ||||
|    const uint8_t *data; | ||||
|    size_t size; | ||||
|    size_t i, j; | ||||
|    int step; | ||||
| }; | ||||
| 
 | ||||
| void rwav_init(rwav_iterator_t* iter, rwav_t* out, const void* buf, size_t size) | ||||
| { | ||||
|    iter->out    = out; | ||||
|    iter->data   = (const uint8_t*)buf; | ||||
|    iter->size   = size; | ||||
|    iter->step   = ITER_BEGIN; | ||||
| 
 | ||||
|    out->samples = NULL; | ||||
| } | ||||
| 
 | ||||
| enum rwav_state rwav_iterate(rwav_iterator_t *iter) | ||||
| { | ||||
|    size_t s; | ||||
|    uint16_t *u16       = NULL; | ||||
|    void *samples       = NULL; | ||||
|    rwav_t *rwav        = iter->out; | ||||
|    const uint8_t *data = iter->data; | ||||
| 
 | ||||
|    switch (iter->step) | ||||
|    { | ||||
|       case ITER_BEGIN: | ||||
|          if (iter->size < 44) | ||||
|             return RWAV_ITERATE_ERROR; /* buffer is smaller than an empty wave file */ | ||||
| 
 | ||||
|          if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') | ||||
|             return RWAV_ITERATE_ERROR; | ||||
| 
 | ||||
|          if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') | ||||
|             return RWAV_ITERATE_ERROR; | ||||
| 
 | ||||
|          if (data[12] != 'f' || data[13] != 'm' || data[14] != 't' || data[15] != ' ') | ||||
|             return RWAV_ITERATE_ERROR; /* we don't support non-PCM or compressed data */ | ||||
| 
 | ||||
|          if (data[16] != 16 || data[17] != 0 || data[18] != 0 || data[19] != 0) | ||||
|             return RWAV_ITERATE_ERROR; | ||||
| 
 | ||||
|          if (data[20] != 1 || data[21] != 0) | ||||
|             return RWAV_ITERATE_ERROR; /* we don't support non-PCM or compressed data */ | ||||
| 
 | ||||
|          if (data[36] != 'd' || data[37] != 'a' || data[38] != 't' || data[39] != 'a') | ||||
|             return RWAV_ITERATE_ERROR; | ||||
| 
 | ||||
|          rwav->bitspersample = data[34] | data[35] << 8; | ||||
| 
 | ||||
|          if (rwav->bitspersample != 8 && rwav->bitspersample != 16) | ||||
|             return RWAV_ITERATE_ERROR; /* we only support 8 and 16 bps */ | ||||
| 
 | ||||
|          rwav->subchunk2size = data[40] | data[41] << 8 | data[42] << 16 | data[43] << 24; | ||||
| 
 | ||||
|          if (rwav->subchunk2size > iter->size - 44) | ||||
|             return RWAV_ITERATE_ERROR; /* too few bytes in buffer */ | ||||
| 
 | ||||
|          samples = malloc(rwav->subchunk2size); | ||||
| 
 | ||||
|          if (samples == NULL) | ||||
|             return RWAV_ITERATE_ERROR; | ||||
| 
 | ||||
|          rwav->numchannels = data[22] | data[23] << 8; | ||||
|          rwav->numsamples  = rwav->subchunk2size * 8 / rwav->bitspersample / rwav->numchannels; | ||||
|          rwav->samplerate  = data[24] | data[25] << 8 | data[26] << 16 | data[27] << 24; | ||||
|          rwav->samples     = samples; | ||||
| 
 | ||||
|          iter->step = ITER_COPY_SAMPLES; | ||||
|          return RWAV_ITERATE_MORE; | ||||
| 
 | ||||
|       case ITER_COPY_SAMPLES: | ||||
|          iter->i = 0; | ||||
| 
 | ||||
|          if (rwav->bitspersample == 8) | ||||
|          { | ||||
|             iter->step = ITER_COPY_SAMPLES_8; | ||||
| 
 | ||||
|             /* TODO/FIXME - what is going on here? */ | ||||
|             case ITER_COPY_SAMPLES_8: | ||||
|             s = rwav->subchunk2size - iter->i; | ||||
| 
 | ||||
|             if (s > RWAV_ITERATE_BUF_SIZE) | ||||
|                s = RWAV_ITERATE_BUF_SIZE; | ||||
| 
 | ||||
|             memcpy((void*)((uint8_t*)rwav->samples + iter->i), (void *)(iter->data + 44 + iter->i), s); | ||||
|             iter->i += s; | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             iter->step = ITER_COPY_SAMPLES_16; | ||||
|             iter->j    = 0; | ||||
| 
 | ||||
|             /* TODO/FIXME - what is going on here? */ | ||||
|             case ITER_COPY_SAMPLES_16: | ||||
|             s = rwav->subchunk2size - iter->i; | ||||
| 
 | ||||
|             if (s > RWAV_ITERATE_BUF_SIZE) | ||||
|                s = RWAV_ITERATE_BUF_SIZE; | ||||
| 
 | ||||
|             u16 = (uint16_t *)rwav->samples; | ||||
| 
 | ||||
|             while (s != 0) | ||||
|             { | ||||
|                u16[iter->j++] = iter->data[44 + iter->i] | iter->data[45 + iter->i] << 8; | ||||
|                iter->i += 2; | ||||
|                s -= 2; | ||||
|             } | ||||
|          } | ||||
| 
 | ||||
|          if (iter->i < rwav->subchunk2size) | ||||
|             return RWAV_ITERATE_MORE; | ||||
|          return RWAV_ITERATE_DONE; | ||||
|    } | ||||
| 
 | ||||
|    return RWAV_ITERATE_ERROR; | ||||
| } | ||||
| 
 | ||||
| enum rwav_state rwav_load(rwav_t* out, const void* buf, size_t size) | ||||
| { | ||||
|    enum rwav_state res; | ||||
|    rwav_iterator_t iter; | ||||
| 
 | ||||
|    iter.out             = NULL; | ||||
|    iter.data            = NULL; | ||||
|    iter.size            = 0; | ||||
|    iter.i               = 0; | ||||
|    iter.j               = 0; | ||||
|    iter.step            = 0; | ||||
| 
 | ||||
|    rwav_init(&iter, out, buf, size); | ||||
| 
 | ||||
|    do | ||||
|    { | ||||
|       res = rwav_iterate(&iter); | ||||
|    }while (res == RWAV_ITERATE_MORE); | ||||
| 
 | ||||
|    return res; | ||||
| } | ||||
| 
 | ||||
| void rwav_free(rwav_t *rwav) | ||||
| { | ||||
|    free((void*)rwav->samples); | ||||
| } | ||||
|  | @ -0,0 +1,488 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (rxml.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| #include <boolean.h> | ||||
| #include <streams/file_stream.h> | ||||
| #include <compat/posix_string.h> | ||||
| #include <string/stdstring.h> | ||||
| 
 | ||||
| #include <formats/rxml.h> | ||||
| 
 | ||||
| struct rxml_document | ||||
| { | ||||
|    struct rxml_node *root_node; | ||||
| }; | ||||
| 
 | ||||
| struct rxml_node *rxml_root_node(rxml_document_t *doc) | ||||
| { | ||||
|    if (doc) | ||||
|       return doc->root_node; | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static void rxml_free_node(struct rxml_node *node) | ||||
| { | ||||
|    struct rxml_node *head = NULL; | ||||
|    struct rxml_attrib_node *attrib_node_head = NULL; | ||||
| 
 | ||||
|    if (!node) | ||||
|       return; | ||||
| 
 | ||||
|    for (head = node->children; head; ) | ||||
|    { | ||||
|       struct rxml_node *next_node = (struct rxml_node*)head->next; | ||||
|       rxml_free_node(head); | ||||
|       head = next_node; | ||||
|    } | ||||
| 
 | ||||
|    for (attrib_node_head = node->attrib; attrib_node_head; ) | ||||
|    { | ||||
|       struct rxml_attrib_node *next_attrib = NULL; | ||||
| 
 | ||||
|       if (!attrib_node_head) | ||||
|          continue; | ||||
| 
 | ||||
|       next_attrib = (struct rxml_attrib_node*)attrib_node_head->next; | ||||
| 
 | ||||
|       if (!next_attrib) | ||||
|          continue; | ||||
| 
 | ||||
|       if (attrib_node_head->attrib) | ||||
|          free(attrib_node_head->attrib); | ||||
|       if (attrib_node_head->value) | ||||
|          free(attrib_node_head->value); | ||||
|       if (attrib_node_head) | ||||
|          free(attrib_node_head); | ||||
| 
 | ||||
|       attrib_node_head = next_attrib; | ||||
|    } | ||||
| 
 | ||||
|    if (node->name) | ||||
|       free(node->name); | ||||
|    if (node->data) | ||||
|       free(node->data); | ||||
|    if (node) | ||||
|       free(node); | ||||
| } | ||||
| 
 | ||||
| static bool validate_header(const char **ptr) | ||||
| { | ||||
|    if (memcmp(*ptr, "<?xml", 5) == 0) | ||||
|    { | ||||
|       const char *eol = strstr(*ptr, "?>\n"); | ||||
|       if (!eol) | ||||
|          return false; | ||||
| 
 | ||||
|       /* Always use UTF-8. Don't really care to check. */ | ||||
|       *ptr = eol + 3; | ||||
|       return true; | ||||
|    } | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static bool range_is_space(const char *begin, const char *end) | ||||
| { | ||||
|    for (; begin < end; begin++) | ||||
|       if (!isspace(*begin)) | ||||
|          return false; | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static void skip_spaces(const char **ptr_) | ||||
| { | ||||
|    const char *ptr = *ptr_; | ||||
|    while (isspace(*ptr)) | ||||
|       ptr++; | ||||
| 
 | ||||
|    *ptr_ = ptr; | ||||
| } | ||||
| 
 | ||||
| static char *strdup_range(const char *begin, const char *end) | ||||
| { | ||||
|    ptrdiff_t len = end - begin; | ||||
|    char *ret = (char*)malloc(len + 1); | ||||
| 
 | ||||
|    if (!ret) | ||||
|       return NULL; | ||||
| 
 | ||||
|    memcpy(ret, begin, len); | ||||
|    ret[len] = '\0'; | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| static char *strdup_range_escape(const char *begin, const char *end) | ||||
| { | ||||
|    /* Escaping is ignored. Assume we don't deal with that. */ | ||||
|    return strdup_range(begin, end); | ||||
| } | ||||
| 
 | ||||
| static struct rxml_attrib_node *rxml_parse_attrs(const char *str) | ||||
| { | ||||
|    char *copy = strdup(str); | ||||
|    if (!copy) | ||||
|       return NULL; | ||||
| 
 | ||||
|    char *last_char = copy + strlen(copy) - 1; | ||||
|    if (*last_char == '/') | ||||
|       *last_char = '\0'; | ||||
| 
 | ||||
|    struct rxml_attrib_node *list = NULL; | ||||
|    struct rxml_attrib_node *tail = NULL; | ||||
| 
 | ||||
|    char *attrib = NULL; | ||||
|    char *value = NULL; | ||||
|    char *save; | ||||
|    const char *elem = strtok_r(copy, " \n\t\f\v\r", &save); | ||||
|    while (elem) | ||||
|    { | ||||
|       const char *eq = strstr(elem, "=\""); | ||||
|       if (!eq) | ||||
|          goto end; | ||||
| 
 | ||||
|       const char *end = strrchr(eq + 2, '\"'); | ||||
|       if (!end || end != (elem + strlen(elem) - 1)) | ||||
|          goto end; | ||||
| 
 | ||||
|       attrib = strdup_range_escape(elem, eq); | ||||
|       value  = strdup_range_escape(eq + 2, end); | ||||
|       if (!attrib || !value) | ||||
|          goto end; | ||||
| 
 | ||||
|       struct rxml_attrib_node *new_node = | ||||
|          (struct rxml_attrib_node*)calloc(1, sizeof(*new_node)); | ||||
|       if (!new_node) | ||||
|          goto end; | ||||
| 
 | ||||
|       new_node->attrib = attrib; | ||||
|       new_node->value  = value; | ||||
|       attrib = NULL; | ||||
|       value = NULL; | ||||
| 
 | ||||
|       if (tail) | ||||
|       { | ||||
|          tail->next = new_node; | ||||
|          tail = new_node; | ||||
|       } | ||||
|       else | ||||
|          list = tail = new_node; | ||||
| 
 | ||||
|       elem = strtok_r(NULL, " \n\t\f\v\r", &save); | ||||
|    } | ||||
| 
 | ||||
| end: | ||||
|    if (copy) | ||||
|       free(copy); | ||||
|    if (attrib) | ||||
|       free(attrib); | ||||
|    if (value) | ||||
|       free(value); | ||||
|    return list; | ||||
| } | ||||
| 
 | ||||
| static char *find_first_space(const char *str) | ||||
| { | ||||
|    while (*str && !isspace(*str)) | ||||
|       str++; | ||||
| 
 | ||||
|    return isspace(*str) ? (char*)str : NULL; | ||||
| } | ||||
| 
 | ||||
| static bool rxml_parse_tag(struct rxml_node *node, const char *str) | ||||
| { | ||||
|    const char *str_ptr = str; | ||||
|    skip_spaces(&str_ptr); | ||||
| 
 | ||||
|    const char *name_end = find_first_space(str_ptr); | ||||
|    if (name_end) | ||||
|    { | ||||
|       node->name = strdup_range(str_ptr, name_end); | ||||
|       if (!node->name || !*node->name) | ||||
|          return false; | ||||
| 
 | ||||
|       node->attrib = rxml_parse_attrs(name_end); | ||||
|       return true; | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       node->name = strdup(str_ptr); | ||||
|       return node->name && *node->name; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static struct rxml_node *rxml_parse_node(const char **ptr_) | ||||
| { | ||||
|    const char *ptr     = NULL; | ||||
|    const char *closing = NULL; | ||||
|    char *str           = NULL; | ||||
|    bool is_closing     = false; | ||||
| 
 | ||||
|    struct rxml_node *node = (struct rxml_node*)calloc(1, sizeof(*node)); | ||||
|    if (!node) | ||||
|       return NULL; | ||||
| 
 | ||||
|    skip_spaces(ptr_); | ||||
| 
 | ||||
|    ptr = *ptr_; | ||||
|    if (*ptr != '<') | ||||
|       goto error; | ||||
| 
 | ||||
|    closing = strchr(ptr, '>'); | ||||
|    if (!closing) | ||||
|       goto error; | ||||
| 
 | ||||
|    str = strdup_range(ptr + 1, closing); | ||||
|    if (!str) | ||||
|       goto error; | ||||
| 
 | ||||
|    if (!rxml_parse_tag(node, str)) | ||||
|       goto error; | ||||
| 
 | ||||
|    /* Are spaces between / and > allowed? */ | ||||
|    is_closing = strstr(ptr, "/>") + 1 == closing; | ||||
| 
 | ||||
|    /* Look for more data. Either child nodes or data. */ | ||||
|    if (!is_closing) | ||||
|    { | ||||
|       size_t closing_tag_size = strlen(node->name) + 4; | ||||
|       char *closing_tag = (char*)malloc(closing_tag_size); | ||||
| 
 | ||||
|       const char *cdata_start = NULL; | ||||
|       const char *child_start = NULL; | ||||
|       const char *closing_start = NULL; | ||||
| 
 | ||||
|       if (!closing_tag) | ||||
|          goto error; | ||||
| 
 | ||||
|       snprintf(closing_tag, closing_tag_size, "</%s>", node->name); | ||||
| 
 | ||||
|       cdata_start   = strstr(closing + 1, "<![CDATA["); | ||||
|       child_start   = strchr(closing + 1, '<'); | ||||
|       closing_start = strstr(closing + 1, closing_tag); | ||||
| 
 | ||||
|       if (!closing_start) | ||||
|       { | ||||
|          free(closing_tag); | ||||
|          goto error; | ||||
|       } | ||||
| 
 | ||||
|       if (cdata_start && range_is_space(closing + 1, cdata_start)) | ||||
|       { | ||||
|          /* CDATA section */ | ||||
|          const char *cdata_end = strstr(cdata_start, "]]>"); | ||||
|          if (!cdata_end) | ||||
|          { | ||||
|             free(closing_tag); | ||||
|             goto error; | ||||
|          } | ||||
| 
 | ||||
|          node->data = strdup_range(cdata_start + | ||||
|                strlen("<![CDATA["), cdata_end); | ||||
|       } | ||||
|       else if (closing_start && closing_start == child_start) /* Simple Data */ | ||||
|          node->data = strdup_range(closing + 1, closing_start); | ||||
|       else | ||||
|       { | ||||
|          /* Parse all child nodes. */ | ||||
|          struct rxml_node *list = NULL; | ||||
|          struct rxml_node *tail = NULL; | ||||
|          const char *first_start = NULL; | ||||
|          const char *first_closing = NULL; | ||||
| 
 | ||||
|          ptr           = child_start; | ||||
|          first_start   = strchr(ptr, '<'); | ||||
|          first_closing = strstr(ptr, "</"); | ||||
| 
 | ||||
|          while ( | ||||
|                 first_start && | ||||
|                 first_closing && | ||||
|                 (first_start < first_closing) | ||||
|                 ) | ||||
|          { | ||||
|             struct rxml_node *new_node = rxml_parse_node(&ptr); | ||||
| 
 | ||||
|             if (!new_node) | ||||
|             { | ||||
|                free(closing_tag); | ||||
|                goto error; | ||||
|             } | ||||
| 
 | ||||
|             if (tail) | ||||
|             { | ||||
|                tail->next = new_node; | ||||
|                tail = new_node; | ||||
|             } | ||||
|             else | ||||
|                list = tail = new_node; | ||||
| 
 | ||||
|             first_start   = strchr(ptr, '<'); | ||||
|             first_closing = strstr(ptr, "</"); | ||||
|          } | ||||
| 
 | ||||
|          node->children = list; | ||||
| 
 | ||||
|          closing_start = strstr(ptr, closing_tag); | ||||
|          if (!closing_start) | ||||
|          { | ||||
|             free(closing_tag); | ||||
|             goto error; | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       *ptr_ = closing_start + strlen(closing_tag); | ||||
|       free(closing_tag); | ||||
|    } | ||||
|    else | ||||
|       *ptr_ = closing + 1; | ||||
| 
 | ||||
|    if (str) | ||||
|       free(str); | ||||
|    return node; | ||||
| 
 | ||||
| error: | ||||
|    if (str) | ||||
|       free(str); | ||||
|    rxml_free_node(node); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| static char *purge_xml_comments(const char *str) | ||||
| { | ||||
|    size_t len = strlen(str); | ||||
|    char *new_str = (char*)malloc(len + 1); | ||||
|    if (!new_str) | ||||
|       return NULL; | ||||
| 
 | ||||
|    new_str[len] = '\0'; | ||||
| 
 | ||||
|    char *copy_dest       = new_str; | ||||
|    const char *copy_src  = str; | ||||
|    for (;;) | ||||
|    { | ||||
|       ptrdiff_t copy_len; | ||||
|       const char *comment_start = strstr(copy_src, "<!--"); | ||||
|       const char *comment_end   = strstr(copy_src, "-->"); | ||||
| 
 | ||||
|       if (!comment_start || !comment_end) | ||||
|          break; | ||||
| 
 | ||||
|       copy_len = comment_start - copy_src; | ||||
|       memcpy(copy_dest, copy_src, copy_len); | ||||
| 
 | ||||
|       copy_dest += copy_len; | ||||
|       copy_src   = comment_end + strlen("-->"); | ||||
|    } | ||||
| 
 | ||||
|    /* Avoid strcpy() as OpenBSD is anal and hates you
 | ||||
|     * for using it even when it's perfectly safe. */ | ||||
|    len = strlen(copy_src); | ||||
|    memcpy(copy_dest, copy_src, len); | ||||
|    copy_dest[len] = '\0'; | ||||
| 
 | ||||
|    return new_str; | ||||
| } | ||||
| 
 | ||||
| rxml_document_t *rxml_load_document(const char *path) | ||||
| { | ||||
|    char *memory_buffer     = NULL; | ||||
|    char *new_memory_buffer = NULL; | ||||
|    const char *mem_ptr     = NULL; | ||||
|    long len                = 0; | ||||
|    RFILE *file             = filestream_open(path, | ||||
|          RETRO_VFS_FILE_ACCESS_READ, | ||||
|          RETRO_VFS_FILE_ACCESS_HINT_NONE); | ||||
|    if (!file) | ||||
|       return NULL; | ||||
| 
 | ||||
|    rxml_document_t *doc = (rxml_document_t*)calloc(1, sizeof(*doc)); | ||||
|    if (!doc) | ||||
|       goto error; | ||||
| 
 | ||||
|    len           = filestream_get_size(file); | ||||
|    memory_buffer = (char*)malloc(len + 1); | ||||
|    if (!memory_buffer) | ||||
|       goto error; | ||||
| 
 | ||||
|    memory_buffer[len] = '\0'; | ||||
|    if (filestream_read(file, memory_buffer, len) != (size_t)len) | ||||
|       goto error; | ||||
| 
 | ||||
|    filestream_close(file); | ||||
|    file = NULL; | ||||
| 
 | ||||
|    mem_ptr = memory_buffer; | ||||
| 
 | ||||
|    if (!validate_header(&mem_ptr)) | ||||
|       goto error; | ||||
| 
 | ||||
|    new_memory_buffer = purge_xml_comments(mem_ptr); | ||||
|    if (!new_memory_buffer) | ||||
|       goto error; | ||||
| 
 | ||||
|    free(memory_buffer); | ||||
|    mem_ptr = memory_buffer = new_memory_buffer; | ||||
| 
 | ||||
|    doc->root_node = rxml_parse_node(&mem_ptr); | ||||
|    if (!doc->root_node) | ||||
|       goto error; | ||||
| 
 | ||||
|    free(memory_buffer); | ||||
|    return doc; | ||||
| 
 | ||||
| error: | ||||
|    free(memory_buffer); | ||||
|    filestream_close(file); | ||||
|    rxml_free_document(doc); | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
| void rxml_free_document(rxml_document_t *doc) | ||||
| { | ||||
|    if (!doc) | ||||
|       return; | ||||
| 
 | ||||
|    if (doc->root_node) | ||||
|       rxml_free_node(doc->root_node); | ||||
| 
 | ||||
|    free(doc); | ||||
| } | ||||
| 
 | ||||
| char *rxml_node_attrib(struct rxml_node *node, const char *attrib) | ||||
| { | ||||
|    struct rxml_attrib_node *attribs = NULL; | ||||
|    for (attribs = node->attrib; attribs; attribs = attribs->next) | ||||
|    { | ||||
|       if (string_is_equal(attrib, attribs->attrib)) | ||||
|          return attribs->value; | ||||
|    } | ||||
| 
 | ||||
|    return NULL; | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,27 @@ | |||
| TARGET := rxml | ||||
| 
 | ||||
| LIBRETRO_XML_DIR  := .. | ||||
| LIBRETRO_COMM_DIR := ../../.. | ||||
| 
 | ||||
| SOURCES := \
 | ||||
| 	rxml_test.c \
 | ||||
| 	$(LIBRETRO_XML_DIR)/rxml.c \
 | ||||
| 	$(LIBRETRO_COMM_DIR)/streams/file_stream.c | ||||
| 
 | ||||
| OBJS := $(SOURCES:.c=.o) | ||||
| 
 | ||||
| CFLAGS += -DRXML_TEST -Wall -pedantic -std=gnu99 -g -I$(LIBRETRO_COMM_DIR)/include | ||||
| 
 | ||||
| all: $(TARGET) | ||||
| 
 | ||||
| %.o: %.c | ||||
| 	$(CC) -c -o $@ $< $(CFLAGS) | ||||
| 
 | ||||
| $(TARGET): $(OBJS) | ||||
| 	$(CC) -o $@ $^ $(LDFLAGS) | ||||
| 
 | ||||
| clean: | ||||
| 	rm -f $(TARGET) $(OBJS) | ||||
| 
 | ||||
| .PHONY: clean | ||||
| 
 | ||||
|  | @ -0,0 +1,67 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (rxml_test.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <formats/rxml.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| static void print_siblings(struct rxml_node *node, unsigned level) | ||||
| { | ||||
|    fprintf(stderr, "\n%*sName: %s\n", level * 4, "", node->name); | ||||
|    if (node->data) | ||||
|       fprintf(stderr, "%*sData: %s\n", level * 4, "", node->data); | ||||
| 
 | ||||
|    for (const struct rxml_attrib_node *attrib = | ||||
|          node->attrib; attrib; attrib = attrib->next) | ||||
|       fprintf(stderr, "%*s  Attrib: %s = %s\n", level * 4, "", | ||||
|             attrib->attrib, attrib->value); | ||||
| 
 | ||||
|    if (node->children) | ||||
|       print_siblings(node->children, level + 1); | ||||
| 
 | ||||
|    if (node->next) | ||||
|       print_siblings(node->next, level); | ||||
| } | ||||
| 
 | ||||
| static void rxml_log_document(const char *path) | ||||
| { | ||||
|    rxml_document_t *doc = rxml_load_document(path); | ||||
|    if (!doc) | ||||
|    { | ||||
|       fprintf(stderr, "rxml: Failed to load document: %s\n", path); | ||||
|       return; | ||||
|    } | ||||
| 
 | ||||
|    print_siblings(rxml_root_node(doc), 0); | ||||
|    rxml_free_document(doc); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|    if (argc != 2) | ||||
|    { | ||||
|       fprintf(stderr, "Usage: %s <path>\n", argv[0]); | ||||
|       return 1; | ||||
|    } | ||||
| 
 | ||||
|    rxml_log_document(argv[1]); | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,335 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (gl_capabilities.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <math.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <boolean.h> | ||||
| 
 | ||||
| #include <glsym/glsym.h> | ||||
| 
 | ||||
| #include <gfx/gl_capabilities.h> | ||||
| 
 | ||||
| static bool gl_core_context       = false; | ||||
| 
 | ||||
| bool gl_query_core_context_in_use(void) | ||||
| { | ||||
|    return gl_core_context; | ||||
| } | ||||
| 
 | ||||
| void gl_query_core_context_set(bool set) | ||||
| { | ||||
|    gl_core_context     =  set; | ||||
| } | ||||
| 
 | ||||
| void gl_query_core_context_unset(void) | ||||
| { | ||||
|    gl_core_context = false; | ||||
| } | ||||
| 
 | ||||
| bool gl_query_extension(const char *ext) | ||||
| { | ||||
|    bool ret = false; | ||||
| 
 | ||||
|    if (gl_query_core_context_in_use()) | ||||
|    { | ||||
| #ifdef GL_NUM_EXTENSIONS | ||||
|       GLint i; | ||||
|       GLint exts = 0; | ||||
|       glGetIntegerv(GL_NUM_EXTENSIONS, &exts); | ||||
|       for (i = 0; i < exts; i++) | ||||
|       { | ||||
|          const char *str = (const char*)glGetStringi(GL_EXTENSIONS, i); | ||||
|          if (str && strstr(str, ext)) | ||||
|          { | ||||
|             ret = true; | ||||
|             break; | ||||
|          } | ||||
|       } | ||||
| #endif | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       const char *str = (const char*)glGetString(GL_EXTENSIONS); | ||||
|       ret = str && strstr(str, ext); | ||||
|    } | ||||
| 
 | ||||
|    return ret; | ||||
| } | ||||
| 
 | ||||
| bool gl_check_error(char **error_string) | ||||
| { | ||||
|    int error = glGetError(); | ||||
|    switch (error) | ||||
|    { | ||||
|       case GL_INVALID_ENUM: | ||||
|          *error_string = strdup("GL: Invalid enum."); | ||||
|          break; | ||||
|       case GL_INVALID_VALUE: | ||||
|          *error_string = strdup("GL: Invalid value."); | ||||
|          break; | ||||
|       case GL_INVALID_OPERATION: | ||||
|          *error_string = strdup("GL: Invalid operation."); | ||||
|          break; | ||||
|       case GL_OUT_OF_MEMORY: | ||||
|          *error_string = strdup("GL: Out of memory."); | ||||
|          break; | ||||
|       case GL_NO_ERROR: | ||||
|          return true; | ||||
|       default: | ||||
|          *error_string = strdup("Non specified GL error."); | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return false; | ||||
| } | ||||
| 
 | ||||
| bool gl_check_capability(enum gl_capability_enum enum_idx) | ||||
| { | ||||
|    unsigned major       = 0; | ||||
|    unsigned minor       = 0; | ||||
|    const char *vendor   = (const char*)glGetString(GL_VENDOR); | ||||
|    const char *renderer = (const char*)glGetString(GL_RENDERER); | ||||
|    const char *version  = (const char*)glGetString(GL_VERSION); | ||||
| #ifdef HAVE_OPENGLES | ||||
|    if (version && sscanf(version, "OpenGL ES %u.%u", &major, &minor) != 2) | ||||
| #else | ||||
|    if (version && sscanf(version, "%u.%u", &major, &minor) != 2) | ||||
| #endif | ||||
|       major = minor = 0; | ||||
| 
 | ||||
|    (void)vendor; | ||||
|    (void)renderer; | ||||
| 
 | ||||
|    switch (enum_idx) | ||||
|    { | ||||
|       case GL_CAPS_GLES3_SUPPORTED: | ||||
| #if defined(HAVE_OPENGLES) | ||||
|          if (major >= 3) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_EGLIMAGE: | ||||
| #if defined(HAVE_EGL) && defined(HAVE_OPENGLES) | ||||
|          if (glEGLImageTargetTexture2DOES != NULL) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_SYNC: | ||||
| #ifdef HAVE_OPENGLES | ||||
|          if (major >= 3) | ||||
|             return true; | ||||
| #else | ||||
|          if (gl_query_extension("ARB_sync") && | ||||
|                glFenceSync && glDeleteSync && glClientWaitSync) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_MIPMAP: | ||||
|          { | ||||
|             static bool extension_queried = false; | ||||
|             static bool extension         = false; | ||||
| 
 | ||||
|             if (!extension_queried) | ||||
|             { | ||||
|                extension         = gl_query_extension("ARB_framebuffer_object"); | ||||
|                extension_queried = true; | ||||
|             } | ||||
| 
 | ||||
|             if (extension) | ||||
|                return true; | ||||
|          } | ||||
|          break; | ||||
|       case GL_CAPS_VAO: | ||||
| #ifndef HAVE_OPENGLES | ||||
|          if (!gl_query_core_context_in_use() && !gl_query_extension("ARB_vertex_array_object")) | ||||
|             return false; | ||||
| 
 | ||||
|          if (glGenVertexArrays && glBindVertexArray && glDeleteVertexArrays) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_FBO: | ||||
| #if defined(HAVE_PSGL) || defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES3) || defined(HAVE_OPENGLES_3_1) || defined(HAVE_OPENGLES_3_2) | ||||
|          return true; | ||||
| #else | ||||
|          if (     !gl_query_core_context_in_use() | ||||
|                && !gl_query_extension("ARB_framebuffer_object") | ||||
|                && !gl_query_extension("EXT_framebuffer_object")) | ||||
|             return false; | ||||
| 
 | ||||
|          if (gl_query_extension("ARB_framebuffer_object")) | ||||
|             return true; | ||||
| 
 | ||||
|          if (gl_query_extension("EXT_framebuffer_object")) | ||||
|             return true; | ||||
| 
 | ||||
|          if (major >= 3) | ||||
|             return true; | ||||
|          break; | ||||
| #endif | ||||
|       case GL_CAPS_ARGB8: | ||||
| #ifdef HAVE_OPENGLES | ||||
|          if (gl_query_extension("OES_rgb8_rgba8") | ||||
|                || gl_query_extension("ARM_rgba8") | ||||
|                   || major >= 3) | ||||
|             return true; | ||||
| #else | ||||
|          /* TODO/FIXME - implement this for non-GLES? */ | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_DEBUG: | ||||
|          if (gl_query_extension("KHR_debug")) | ||||
|             return true; | ||||
| #ifndef HAVE_OPENGLES | ||||
|          if (gl_query_extension("ARB_debug_output")) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_PACKED_DEPTH_STENCIL: | ||||
|          if (major >= 3) | ||||
|             return true; | ||||
|          if (gl_query_extension("OES_packed_depth_stencil")) | ||||
|             return true; | ||||
|          if (gl_query_extension("EXT_packed_depth_stencil")) | ||||
|             return true; | ||||
|          break; | ||||
|       case GL_CAPS_ES2_COMPAT: | ||||
| #ifndef HAVE_OPENGLES | ||||
|          /* ATI card detected, skipping check for GL_RGB565 support... */ | ||||
|          if (vendor && renderer && (strstr(vendor, "ATI") || strstr(renderer, "ATI"))) | ||||
|             return false; | ||||
| 
 | ||||
|          if (gl_query_extension("ARB_ES2_compatibility")) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_UNPACK_ROW_LENGTH: | ||||
| #ifdef HAVE_OPENGLES | ||||
|          if (major >= 3) | ||||
|             return true; | ||||
| 
 | ||||
|          /* Extension GL_EXT_unpack_subimage, can copy textures faster
 | ||||
|           * than using UNPACK_ROW_LENGTH */ | ||||
|          if (gl_query_extension("GL_EXT_unpack_subimage")) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_FULL_NPOT_SUPPORT: | ||||
|          if (major >= 3) | ||||
|             return true; | ||||
| #ifdef HAVE_OPENGLES | ||||
|          if (gl_query_extension("ARB_texture_non_power_of_two") || | ||||
|                gl_query_extension("OES_texture_npot")) | ||||
|             return true; | ||||
| #else | ||||
|          { | ||||
|             GLint max_texture_size = 0; | ||||
|             GLint max_native_instr = 0; | ||||
|             /* try to detect actual npot support. might fail for older cards. */ | ||||
|             bool  arb_npot         = gl_query_extension("ARB_texture_non_power_of_two"); | ||||
|             bool  arb_frag_program = gl_query_extension("ARB_fragment_program"); | ||||
| 
 | ||||
|             glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); | ||||
| 
 | ||||
| #ifdef GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB | ||||
|             if (arb_frag_program && glGetProgramivARB) | ||||
|                glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, | ||||
|                      GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &max_native_instr); | ||||
| #endif | ||||
| 
 | ||||
|             if (arb_npot && arb_frag_program && | ||||
|                   (max_texture_size >= 8192) && (max_native_instr >= 4096)) | ||||
|                return true; | ||||
|          } | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_SRGB_FBO_ES3: | ||||
| #ifdef HAVE_OPENGLES | ||||
|          if (major >= 3) | ||||
|             return true; | ||||
| #else | ||||
|          break; | ||||
| #endif | ||||
|       case GL_CAPS_SRGB_FBO: | ||||
| #if defined(HAVE_OPENGLES) | ||||
|          if (major >= 3 || gl_query_extension("EXT_sRGB")) | ||||
|             return true; | ||||
| #endif | ||||
|          if (gl_check_capability(GL_CAPS_FBO)) | ||||
|          { | ||||
|             if (   gl_query_core_context_in_use() || | ||||
|                   (gl_query_extension("EXT_texture_sRGB") | ||||
|                    && gl_query_extension("ARB_framebuffer_sRGB")) | ||||
|                ) | ||||
|                return true; | ||||
|          } | ||||
|          break; | ||||
|       case GL_CAPS_FP_FBO: | ||||
|          /* GLES - No extensions for float FBO currently. */ | ||||
| #ifndef HAVE_OPENGLES | ||||
|          if (gl_check_capability(GL_CAPS_FBO)) | ||||
|          { | ||||
|             /* Float FBO is core in 3.2. */ | ||||
|             if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float")) | ||||
|                return true; | ||||
|          } | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_BGRA8888: | ||||
| #ifdef HAVE_OPENGLES | ||||
|          /* There are both APPLE and EXT variants. */ | ||||
|          if (gl_query_extension("BGRA8888") && !strstr(renderer, "VideoCore")) | ||||
|             return true; | ||||
| #else | ||||
|          return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_TEX_STORAGE: | ||||
| #ifdef HAVE_OPENGLES | ||||
|          if (major >= 3) | ||||
|             return true; | ||||
| #else | ||||
|          if (strstr(vendor, "ATI Technologies")) | ||||
|             return false; | ||||
|          if (gl_query_extension("ARB_texture_storage")) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_TEX_STORAGE_EXT: | ||||
| #ifdef TARGET_OS_IPHONE | ||||
|            /* Not working on iOS */ | ||||
|            return false; | ||||
| #else | ||||
|          if (gl_query_extension("EXT_texture_storage")) | ||||
|             return true; | ||||
| #endif | ||||
|          break; | ||||
|       case GL_CAPS_NONE: | ||||
|       default: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    return false; | ||||
| } | ||||
|  | @ -0,0 +1,817 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (pixconv.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| 
 | ||||
| #include <gfx/scaler/pixconv.h> | ||||
| 
 | ||||
| #ifdef SCALER_NO_SIMD | ||||
| #undef __SSE2__ | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
| #include <emmintrin.h> | ||||
| #endif | ||||
| 
 | ||||
| void conv_rgb565_0rgb1555(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    const uint16_t *input = (const uint16_t*)input_; | ||||
|    uint16_t *output = (uint16_t*)output_; | ||||
| 
 | ||||
| #if defined(__SSE2_) | ||||
|    int max_width           = width - 7; | ||||
|    const __m128i hi_mask   = _mm_set1_epi16(0x7fe0); | ||||
|    const __m128i lo_mask   = _mm_set1_epi16(0x1f); | ||||
| #endif | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 1, input += in_stride >> 1) | ||||
|    { | ||||
|       int w = 0; | ||||
| #if defined(__SSE2_) | ||||
|       for (; w < max_width; w += 8) | ||||
|       { | ||||
|          const __m128i in = _mm_loadu_si128((const __m128i*)(input + w)); | ||||
|          __m128i hi = _mm_and_si128(_mm_slli_epi16(in, 1), hi_mask); | ||||
|          __m128i lo = _mm_and_si128(in, lo_mask); | ||||
|          _mm_storeu_si128((__m128i*)(output + w), _mm_or_si128(hi, lo)); | ||||
|       } | ||||
| #endif | ||||
| 
 | ||||
|       for (; w < width; w++) | ||||
|       { | ||||
|          uint16_t col = input[w]; | ||||
|          uint16_t hi  = (col >> 1) & 0x7fe0; | ||||
|          uint16_t lo  = col & 0x1f; | ||||
|          output[w]    = hi | lo; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_0rgb1555_rgb565(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    const uint16_t *input   = (const uint16_t*)input_; | ||||
|    uint16_t *output        = (uint16_t*)output_; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|    int max_width           = width - 7; | ||||
| 
 | ||||
|    const __m128i hi_mask   = _mm_set1_epi16( | ||||
|          (int16_t)((0x1f << 11) | (0x1f << 6))); | ||||
|    const __m128i lo_mask   = _mm_set1_epi16(0x1f); | ||||
|    const __m128i glow_mask = _mm_set1_epi16(1 << 5); | ||||
| #endif | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 1, input += in_stride >> 1) | ||||
|    { | ||||
|       int w = 0; | ||||
| #if defined(__SSE2__) | ||||
|       for (; w < max_width; w += 8) | ||||
|       { | ||||
|          const __m128i in = _mm_loadu_si128((const __m128i*)(input + w)); | ||||
|          __m128i rg   = _mm_and_si128(_mm_slli_epi16(in, 1), hi_mask); | ||||
|          __m128i b    = _mm_and_si128(in, lo_mask); | ||||
|          __m128i glow = _mm_and_si128(_mm_srli_epi16(in, 4), glow_mask); | ||||
|          _mm_storeu_si128((__m128i*)(output + w), | ||||
|                _mm_or_si128(rg, _mm_or_si128(b, glow))); | ||||
|       } | ||||
| #endif | ||||
| 
 | ||||
|       for (; w < width; w++) | ||||
|       { | ||||
|          uint16_t col  = input[w]; | ||||
|          uint16_t rg   = (col << 1) & ((0x1f << 11) | (0x1f << 6)); | ||||
|          uint16_t b    = col & 0x1f; | ||||
|          uint16_t glow = (col >> 4) & (1 << 5); | ||||
|          output[w]     = rg | b | glow; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_0rgb1555_argb8888(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    const uint16_t *input = (const uint16_t*)input_; | ||||
|    uint32_t *output      = (uint32_t*)output_; | ||||
| 
 | ||||
| #ifdef __SSE2__ | ||||
|    const __m128i pix_mask_r  = _mm_set1_epi16(0x1f << 10); | ||||
|    const __m128i pix_mask_gb = _mm_set1_epi16(0x1f <<  5); | ||||
|    const __m128i mul15_mid   = _mm_set1_epi16(0x4200); | ||||
|    const __m128i mul15_hi    = _mm_set1_epi16(0x0210); | ||||
|    const __m128i a           = _mm_set1_epi16(0x00ff); | ||||
| 
 | ||||
|    int max_width = width - 7; | ||||
| #endif | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 2, input += in_stride >> 1) | ||||
|    { | ||||
|       int w = 0; | ||||
| #ifdef __SSE2__ | ||||
|       for (; w < max_width; w += 8) | ||||
|       { | ||||
|          __m128i res_lo_bg, res_hi_bg; | ||||
|          __m128i res_lo_ra, res_hi_ra; | ||||
|          __m128i res_lo, res_hi; | ||||
|          const __m128i in = _mm_loadu_si128((const __m128i*)(input + w)); | ||||
|          __m128i r = _mm_and_si128(in, pix_mask_r); | ||||
|          __m128i g = _mm_and_si128(in, pix_mask_gb); | ||||
|          __m128i b = _mm_and_si128(_mm_slli_epi16(in, 5), pix_mask_gb); | ||||
| 
 | ||||
|          r = _mm_mulhi_epi16(r, mul15_hi); | ||||
|          g = _mm_mulhi_epi16(g, mul15_mid); | ||||
|          b = _mm_mulhi_epi16(b, mul15_mid); | ||||
| 
 | ||||
|          res_lo_bg = _mm_unpacklo_epi8(b, g); | ||||
|          res_hi_bg = _mm_unpackhi_epi8(b, g); | ||||
|          res_lo_ra = _mm_unpacklo_epi8(r, a); | ||||
|          res_hi_ra = _mm_unpackhi_epi8(r, a); | ||||
| 
 | ||||
|          res_lo = _mm_or_si128(res_lo_bg, | ||||
|                _mm_slli_si128(res_lo_ra, 2)); | ||||
|          res_hi = _mm_or_si128(res_hi_bg, | ||||
|                _mm_slli_si128(res_hi_ra, 2)); | ||||
| 
 | ||||
|          _mm_storeu_si128((__m128i*)(output + w + 0), res_lo); | ||||
|          _mm_storeu_si128((__m128i*)(output + w + 4), res_hi); | ||||
|       } | ||||
| #endif | ||||
| 
 | ||||
|       for (; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          uint32_t r   = (col >> 10) & 0x1f; | ||||
|          uint32_t g   = (col >>  5) & 0x1f; | ||||
|          uint32_t b   = (col >>  0) & 0x1f; | ||||
|          r            = (r << 3) | (r >> 2); | ||||
|          g            = (g << 3) | (g >> 2); | ||||
|          b            = (b << 3) | (b >> 2); | ||||
| 
 | ||||
|          output[w]    = (0xffu << 24) | (r << 16) | (g << 8) | (b << 0); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_rgb565_argb8888(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    const uint16_t *input    = (const uint16_t*)input_; | ||||
|    uint32_t *output         = (uint32_t*)output_; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|    const __m128i pix_mask_r = _mm_set1_epi16(0x1f << 10); | ||||
|    const __m128i pix_mask_g = _mm_set1_epi16(0x3f <<  5); | ||||
|    const __m128i pix_mask_b = _mm_set1_epi16(0x1f <<  5); | ||||
|    const __m128i mul16_r    = _mm_set1_epi16(0x0210); | ||||
|    const __m128i mul16_g    = _mm_set1_epi16(0x2080); | ||||
|    const __m128i mul16_b    = _mm_set1_epi16(0x4200); | ||||
|    const __m128i a          = _mm_set1_epi16(0x00ff); | ||||
| 
 | ||||
|    int max_width            = width - 7; | ||||
| #endif | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 2, input += in_stride >> 1) | ||||
|    { | ||||
|       int w = 0; | ||||
| #if defined(__SSE2__) | ||||
|       for (; w < max_width; w += 8) | ||||
|       { | ||||
|          __m128i res_lo, res_hi; | ||||
|          __m128i res_lo_bg, res_hi_bg, res_lo_ra, res_hi_ra; | ||||
|          const __m128i in = _mm_loadu_si128((const __m128i*)(input + w)); | ||||
|          __m128i        r = _mm_and_si128(_mm_srli_epi16(in, 1), pix_mask_r); | ||||
|          __m128i        g = _mm_and_si128(in, pix_mask_g); | ||||
|          __m128i        b = _mm_and_si128(_mm_slli_epi16(in, 5), pix_mask_b); | ||||
| 
 | ||||
|          r                = _mm_mulhi_epi16(r, mul16_r); | ||||
|          g                = _mm_mulhi_epi16(g, mul16_g); | ||||
|          b                = _mm_mulhi_epi16(b, mul16_b); | ||||
| 
 | ||||
|          res_lo_bg        = _mm_unpacklo_epi8(b, g); | ||||
|          res_hi_bg        = _mm_unpackhi_epi8(b, g); | ||||
|          res_lo_ra        = _mm_unpacklo_epi8(r, a); | ||||
|          res_hi_ra        = _mm_unpackhi_epi8(r, a); | ||||
| 
 | ||||
|          res_lo           = _mm_or_si128(res_lo_bg, | ||||
|                _mm_slli_si128(res_lo_ra, 2)); | ||||
|          res_hi           = _mm_or_si128(res_hi_bg, | ||||
|                _mm_slli_si128(res_hi_ra, 2)); | ||||
| 
 | ||||
|          _mm_storeu_si128((__m128i*)(output + w + 0), res_lo); | ||||
|          _mm_storeu_si128((__m128i*)(output + w + 4), res_hi); | ||||
|       } | ||||
| #endif | ||||
| 
 | ||||
|       for (; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          uint32_t r   = (col >> 11) & 0x1f; | ||||
|          uint32_t g   = (col >>  5) & 0x3f; | ||||
|          uint32_t b   = (col >>  0) & 0x1f; | ||||
|          r            = (r << 3) | (r >> 2); | ||||
|          g            = (g << 2) | (g >> 4); | ||||
|          b            = (b << 3) | (b >> 2); | ||||
| 
 | ||||
|          output[w]    = (0xffu << 24) | (r << 16) | (g << 8) | (b << 0); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_argb8888_rgba4444(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h, w; | ||||
|    const uint32_t *input = (const uint32_t*)input_; | ||||
|    uint16_t *output      = (uint16_t*)output_; | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 2, input += in_stride >> 1) | ||||
|    { | ||||
|       for (w = 0; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          uint32_t r   = (col >> 16) & 0xf; | ||||
|          uint32_t g   = (col >>  8) & 0xf; | ||||
|          uint32_t b   = (col) & 0xf; | ||||
|          uint32_t a   = (col >>  24) & 0xf; | ||||
|          r            = (r >> 4) | r; | ||||
|          g            = (g >> 4) | g; | ||||
|          b            = (b >> 4) | b; | ||||
|          a            = (a >> 4) | a; | ||||
| 
 | ||||
|          output[w]    = (r << 12) | (g << 8) | (b << 4) | a; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_rgba4444_argb8888(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h, w; | ||||
|    const uint16_t *input = (const uint16_t*)input_; | ||||
|    uint32_t *output      = (uint32_t*)output_; | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 2, input += in_stride >> 1) | ||||
|    { | ||||
|       for (w = 0; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          uint32_t r   = (col >> 12) & 0xf; | ||||
|          uint32_t g   = (col >>  8) & 0xf; | ||||
|          uint32_t b   = (col >>  4) & 0xf; | ||||
|          uint32_t a   = (col >>  0) & 0xf; | ||||
|          r            = (r << 4) | r; | ||||
|          g            = (g << 4) | g; | ||||
|          b            = (b << 4) | b; | ||||
|          a            = (a << 4) | a; | ||||
| 
 | ||||
|          output[w]    = (a << 24) | (r << 16) | (g << 8) | (b << 0); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_rgba4444_rgb565(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h, w; | ||||
|    const uint16_t *input = (const uint16_t*)input_; | ||||
|    uint16_t *output      = (uint16_t*)output_; | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 1, input += in_stride >> 1) | ||||
|    { | ||||
|       for (w = 0; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          uint32_t r   = (col >> 12) & 0xf; | ||||
|          uint32_t g   = (col >>  8) & 0xf; | ||||
|          uint32_t b   = (col >>  4) & 0xf; | ||||
| 
 | ||||
|          output[w]    = (r << 12) | (g << 7) | (b << 1); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
| /* :( TODO: Make this saner. */ | ||||
| static INLINE void store_bgr24_sse2(void *output, __m128i a, | ||||
|       __m128i b, __m128i c, __m128i d) | ||||
| { | ||||
|    const __m128i mask_0 = _mm_set_epi32(0, 0, 0, 0x00ffffff); | ||||
|    const __m128i mask_1 = _mm_set_epi32(0, 0, 0x00ffffff, 0); | ||||
|    const __m128i mask_2 = _mm_set_epi32(0, 0x00ffffff, 0, 0); | ||||
|    const __m128i mask_3 = _mm_set_epi32(0x00ffffff, 0, 0, 0); | ||||
| 
 | ||||
|    __m128i a0 = _mm_and_si128(a, mask_0); | ||||
|    __m128i a1 = _mm_srli_si128(_mm_and_si128(a, mask_1),  1); | ||||
|    __m128i a2 = _mm_srli_si128(_mm_and_si128(a, mask_2),  2); | ||||
|    __m128i a3 = _mm_srli_si128(_mm_and_si128(a, mask_3),  3); | ||||
|    __m128i a4 = _mm_slli_si128(_mm_and_si128(b, mask_0), 12); | ||||
|    __m128i a5 = _mm_slli_si128(_mm_and_si128(b, mask_1), 11); | ||||
| 
 | ||||
|    __m128i b0 = _mm_srli_si128(_mm_and_si128(b, mask_1), 5); | ||||
|    __m128i b1 = _mm_srli_si128(_mm_and_si128(b, mask_2), 6); | ||||
|    __m128i b2 = _mm_srli_si128(_mm_and_si128(b, mask_3), 7); | ||||
|    __m128i b3 = _mm_slli_si128(_mm_and_si128(c, mask_0), 8); | ||||
|    __m128i b4 = _mm_slli_si128(_mm_and_si128(c, mask_1), 7); | ||||
|    __m128i b5 = _mm_slli_si128(_mm_and_si128(c, mask_2), 6); | ||||
| 
 | ||||
|    __m128i c0 = _mm_srli_si128(_mm_and_si128(c, mask_2), 10); | ||||
|    __m128i c1 = _mm_srli_si128(_mm_and_si128(c, mask_3), 11); | ||||
|    __m128i c2 = _mm_slli_si128(_mm_and_si128(d, mask_0),  4); | ||||
|    __m128i c3 = _mm_slli_si128(_mm_and_si128(d, mask_1),  3); | ||||
|    __m128i c4 = _mm_slli_si128(_mm_and_si128(d, mask_2),  2); | ||||
|    __m128i c5 = _mm_slli_si128(_mm_and_si128(d, mask_3),  1); | ||||
| 
 | ||||
|    __m128i *out = (__m128i*)output; | ||||
| 
 | ||||
|    _mm_storeu_si128(out + 0, | ||||
|          _mm_or_si128(a0, _mm_or_si128(a1, _mm_or_si128(a2, | ||||
|                   _mm_or_si128(a3, _mm_or_si128(a4, a5)))))); | ||||
| 
 | ||||
|    _mm_storeu_si128(out + 1, | ||||
|          _mm_or_si128(b0, _mm_or_si128(b1, _mm_or_si128(b2, | ||||
|                   _mm_or_si128(b3, _mm_or_si128(b4, b5)))))); | ||||
| 
 | ||||
|    _mm_storeu_si128(out + 2, | ||||
|          _mm_or_si128(c0, _mm_or_si128(c1, _mm_or_si128(c2, | ||||
|                   _mm_or_si128(c3, _mm_or_si128(c4, c5)))))); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void conv_0rgb1555_bgr24(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    const uint16_t *input     = (const uint16_t*)input_; | ||||
|    uint8_t *output           = (uint8_t*)output_; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|    const __m128i pix_mask_r  = _mm_set1_epi16(0x1f << 10); | ||||
|    const __m128i pix_mask_gb = _mm_set1_epi16(0x1f <<  5); | ||||
|    const __m128i mul15_mid   = _mm_set1_epi16(0x4200); | ||||
|    const __m128i mul15_hi    = _mm_set1_epi16(0x0210); | ||||
|    const __m128i a           = _mm_set1_epi16(0x00ff); | ||||
| 
 | ||||
|    int max_width             = width - 15; | ||||
| #endif | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride, input += in_stride >> 1) | ||||
|    { | ||||
|       uint8_t *out = output; | ||||
|       int   w = 0; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|       for (; w < max_width; w += 16, out += 48) | ||||
|       { | ||||
|          __m128i res_lo_bg0, res_lo_bg1, res_hi_bg0, res_hi_bg1, | ||||
|                  res_lo_ra0, res_lo_ra1, res_hi_ra0, res_hi_ra1, | ||||
|                  res_lo0, res_lo1, res_hi0, res_hi1; | ||||
|          const __m128i in0 = _mm_loadu_si128((const __m128i*)(input + w + 0)); | ||||
|          const __m128i in1 = _mm_loadu_si128((const __m128i*)(input + w + 8)); | ||||
|          __m128i r0        = _mm_and_si128(in0, pix_mask_r); | ||||
|          __m128i r1        = _mm_and_si128(in1, pix_mask_r); | ||||
|          __m128i g0        = _mm_and_si128(in0, pix_mask_gb); | ||||
|          __m128i g1        = _mm_and_si128(in1, pix_mask_gb); | ||||
|          __m128i b0        = _mm_and_si128(_mm_slli_epi16(in0, 5), pix_mask_gb); | ||||
|          __m128i b1        = _mm_and_si128(_mm_slli_epi16(in1, 5), pix_mask_gb); | ||||
| 
 | ||||
|          r0                = _mm_mulhi_epi16(r0, mul15_hi); | ||||
|          r1                = _mm_mulhi_epi16(r1, mul15_hi); | ||||
|          g0                = _mm_mulhi_epi16(g0, mul15_mid); | ||||
|          g1                = _mm_mulhi_epi16(g1, mul15_mid); | ||||
|          b0                = _mm_mulhi_epi16(b0, mul15_mid); | ||||
|          b1                = _mm_mulhi_epi16(b1, mul15_mid); | ||||
| 
 | ||||
|          res_lo_bg0        = _mm_unpacklo_epi8(b0, g0); | ||||
|          res_lo_bg1        = _mm_unpacklo_epi8(b1, g1); | ||||
|          res_hi_bg0        = _mm_unpackhi_epi8(b0, g0); | ||||
|          res_hi_bg1        = _mm_unpackhi_epi8(b1, g1); | ||||
|          res_lo_ra0        = _mm_unpacklo_epi8(r0, a); | ||||
|          res_lo_ra1        = _mm_unpacklo_epi8(r1, a); | ||||
|          res_hi_ra0        = _mm_unpackhi_epi8(r0, a); | ||||
|          res_hi_ra1        = _mm_unpackhi_epi8(r1, a); | ||||
| 
 | ||||
|          res_lo0           = _mm_or_si128(res_lo_bg0, | ||||
|                _mm_slli_si128(res_lo_ra0, 2)); | ||||
|          res_lo1           = _mm_or_si128(res_lo_bg1, | ||||
|                _mm_slli_si128(res_lo_ra1, 2)); | ||||
|          res_hi0           = _mm_or_si128(res_hi_bg0, | ||||
|                _mm_slli_si128(res_hi_ra0, 2)); | ||||
|          res_hi1           = _mm_or_si128(res_hi_bg1, | ||||
|                _mm_slli_si128(res_hi_ra1, 2)); | ||||
| 
 | ||||
|          /* Non-POT pixel sizes for the loss */ | ||||
|          store_bgr24_sse2(out, res_lo0, res_hi0, res_lo1, res_hi1); | ||||
|       } | ||||
| #endif | ||||
| 
 | ||||
|       for (; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          uint32_t b   = (col >>  0) & 0x1f; | ||||
|          uint32_t g   = (col >>  5) & 0x1f; | ||||
|          uint32_t r   = (col >> 10) & 0x1f; | ||||
|          b            = (b << 3) | (b >> 2); | ||||
|          g            = (g << 3) | (g >> 2); | ||||
|          r            = (r << 3) | (r >> 2); | ||||
| 
 | ||||
|          *out++       = b; | ||||
|          *out++       = g; | ||||
|          *out++       = r; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_rgb565_bgr24(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    const uint16_t *input    = (const uint16_t*)input_; | ||||
|    uint8_t *output          = (uint8_t*)output_; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|    const __m128i pix_mask_r = _mm_set1_epi16(0x1f << 10); | ||||
|    const __m128i pix_mask_g = _mm_set1_epi16(0x3f <<  5); | ||||
|    const __m128i pix_mask_b = _mm_set1_epi16(0x1f <<  5); | ||||
|    const __m128i mul16_r    = _mm_set1_epi16(0x0210); | ||||
|    const __m128i mul16_g    = _mm_set1_epi16(0x2080); | ||||
|    const __m128i mul16_b    = _mm_set1_epi16(0x4200); | ||||
|    const __m128i a          = _mm_set1_epi16(0x00ff); | ||||
| 
 | ||||
|    int max_width            = width - 15; | ||||
| #endif | ||||
| 
 | ||||
|    for (h = 0; h < height; h++, output += out_stride, input += in_stride >> 1) | ||||
|    { | ||||
|       uint8_t *out = output; | ||||
|       int        w = 0; | ||||
| #if defined(__SSE2__) | ||||
|       for (; w < max_width; w += 16, out += 48) | ||||
|       { | ||||
|          __m128i res_lo_bg0, res_hi_bg0, res_lo_ra0, res_hi_ra0; | ||||
|          __m128i res_lo_bg1, res_hi_bg1, res_lo_ra1, res_hi_ra1; | ||||
|          __m128i res_lo0, res_hi0, res_lo1, res_hi1; | ||||
|          const __m128i in0 = _mm_loadu_si128((const __m128i*)(input + w)); | ||||
|          const __m128i in1 = _mm_loadu_si128((const __m128i*)(input + w + 8)); | ||||
|          __m128i r0 = _mm_and_si128(_mm_srli_epi16(in0, 1), pix_mask_r); | ||||
|          __m128i g0 = _mm_and_si128(in0, pix_mask_g); | ||||
|          __m128i b0 = _mm_and_si128(_mm_slli_epi16(in0, 5), pix_mask_b); | ||||
|          __m128i r1 = _mm_and_si128(_mm_srli_epi16(in1, 1), pix_mask_r); | ||||
|          __m128i g1 = _mm_and_si128(in1, pix_mask_g); | ||||
|          __m128i b1 = _mm_and_si128(_mm_slli_epi16(in1, 5), pix_mask_b); | ||||
| 
 | ||||
|          r0         = _mm_mulhi_epi16(r0, mul16_r); | ||||
|          g0         = _mm_mulhi_epi16(g0, mul16_g); | ||||
|          b0         = _mm_mulhi_epi16(b0, mul16_b); | ||||
|          r1         = _mm_mulhi_epi16(r1, mul16_r); | ||||
|          g1         = _mm_mulhi_epi16(g1, mul16_g); | ||||
|          b1         = _mm_mulhi_epi16(b1, mul16_b); | ||||
| 
 | ||||
|          res_lo_bg0 = _mm_unpacklo_epi8(b0, g0); | ||||
|          res_hi_bg0 = _mm_unpackhi_epi8(b0, g0); | ||||
|          res_lo_ra0 = _mm_unpacklo_epi8(r0, a); | ||||
|          res_hi_ra0 = _mm_unpackhi_epi8(r0, a); | ||||
|          res_lo_bg1 = _mm_unpacklo_epi8(b1, g1); | ||||
|          res_hi_bg1 = _mm_unpackhi_epi8(b1, g1); | ||||
|          res_lo_ra1 = _mm_unpacklo_epi8(r1, a); | ||||
|          res_hi_ra1 = _mm_unpackhi_epi8(r1, a); | ||||
| 
 | ||||
|          res_lo0    = _mm_or_si128(res_lo_bg0, | ||||
|                _mm_slli_si128(res_lo_ra0, 2)); | ||||
|          res_hi0    = _mm_or_si128(res_hi_bg0, | ||||
|                _mm_slli_si128(res_hi_ra0, 2)); | ||||
|          res_lo1    = _mm_or_si128(res_lo_bg1, | ||||
|                _mm_slli_si128(res_lo_ra1, 2)); | ||||
|          res_hi1    = _mm_or_si128(res_hi_bg1, | ||||
|                _mm_slli_si128(res_hi_ra1, 2)); | ||||
| 
 | ||||
|          store_bgr24_sse2(out, res_lo0, res_hi0, res_lo1, res_hi1); | ||||
|       } | ||||
| #endif | ||||
| 
 | ||||
|       for (; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          uint32_t r   = (col >> 11) & 0x1f; | ||||
|          uint32_t g   = (col >>  5) & 0x3f; | ||||
|          uint32_t b   = (col >>  0) & 0x1f; | ||||
|          r = (r << 3) | (r >> 2); | ||||
|          g = (g << 2) | (g >> 4); | ||||
|          b = (b << 3) | (b >> 2); | ||||
| 
 | ||||
|          *out++ = b; | ||||
|          *out++ = g; | ||||
|          *out++ = r; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_bgr24_argb8888(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h, w; | ||||
|    const uint8_t *input = (const uint8_t*)input_; | ||||
|    uint32_t *output     = (uint32_t*)output_; | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 2, input += in_stride) | ||||
|    { | ||||
|       const uint8_t *inp = input; | ||||
|       for (w = 0; w < width; w++) | ||||
|       { | ||||
|          uint32_t b = *inp++; | ||||
|          uint32_t g = *inp++; | ||||
|          uint32_t r = *inp++; | ||||
|          output[w]  = (0xffu << 24) | (r << 16) | (g << 8) | (b << 0); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_argb8888_0rgb1555(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h, w; | ||||
|    const uint32_t *input = (const uint32_t*)input_; | ||||
|    uint16_t *output      = (uint16_t*)output_; | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 1, input += in_stride >> 2) | ||||
|    { | ||||
|       for (w = 0; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          uint16_t r   = (col >> 19) & 0x1f; | ||||
|          uint16_t g   = (col >> 11) & 0x1f; | ||||
|          uint16_t b   = (col >>  3) & 0x1f; | ||||
|          output[w]    = (r << 10) | (g << 5) | (b << 0); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_argb8888_bgr24(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    const uint32_t *input = (const uint32_t*)input_; | ||||
|    uint8_t *output       = (uint8_t*)output_; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|    int max_width = width - 15; | ||||
| #endif | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride, input += in_stride >> 2) | ||||
|    { | ||||
|       uint8_t *out = output; | ||||
|       int        w = 0; | ||||
| #if defined(__SSE2__) | ||||
|       for (; w < max_width; w += 16, out += 48) | ||||
|       { | ||||
|          store_bgr24_sse2(out, | ||||
|                _mm_loadu_si128((const __m128i*)(input + w +  0)), | ||||
|                _mm_loadu_si128((const __m128i*)(input + w +  4)), | ||||
|                _mm_loadu_si128((const __m128i*)(input + w +  8)), | ||||
|                _mm_loadu_si128((const __m128i*)(input + w + 12))); | ||||
|       } | ||||
| #endif | ||||
| 
 | ||||
|       for (; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          *out++       = (uint8_t)(col >>  0); | ||||
|          *out++       = (uint8_t)(col >>  8); | ||||
|          *out++       = (uint8_t)(col >> 16); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_argb8888_abgr8888(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h, w; | ||||
|    const uint32_t *input = (const uint32_t*)input_; | ||||
|    uint32_t *output      = (uint32_t*)output_; | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride >> 2, input += in_stride >> 2) | ||||
|    { | ||||
|       for (w = 0; w < width; w++) | ||||
|       { | ||||
|          uint32_t col = input[w]; | ||||
|          output[w]    = ((col << 16) & 0xff0000) | | ||||
|             ((col >> 16) & 0xff) | (col & 0xff00ff00); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| #define YUV_SHIFT 6 | ||||
| #define YUV_OFFSET (1 << (YUV_SHIFT - 1)) | ||||
| #define YUV_MAT_Y (1 << 6) | ||||
| #define YUV_MAT_U_G (-22) | ||||
| #define YUV_MAT_U_B (113) | ||||
| #define YUV_MAT_V_R (90) | ||||
| #define YUV_MAT_V_G (-46) | ||||
| 
 | ||||
| void conv_yuyv_argb8888(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    const uint8_t *input        = (const uint8_t*)input_; | ||||
|    uint32_t *output            = (uint32_t*)output_; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|    const __m128i mask_y        = _mm_set1_epi16(0xffu); | ||||
|    const __m128i mask_u        = _mm_set1_epi32(0xffu << 8); | ||||
|    const __m128i mask_v        = _mm_set1_epi32(0xffu << 24); | ||||
|    const __m128i chroma_offset = _mm_set1_epi16(128); | ||||
|    const __m128i round_offset  = _mm_set1_epi16(YUV_OFFSET); | ||||
| 
 | ||||
|    const __m128i yuv_mul       = _mm_set1_epi16(YUV_MAT_Y); | ||||
|    const __m128i u_g_mul       = _mm_set1_epi16(YUV_MAT_U_G); | ||||
|    const __m128i u_b_mul       = _mm_set1_epi16(YUV_MAT_U_B); | ||||
|    const __m128i v_r_mul       = _mm_set1_epi16(YUV_MAT_V_R); | ||||
|    const __m128i v_g_mul       = _mm_set1_epi16(YUV_MAT_V_G); | ||||
|    const __m128i a             = _mm_cmpeq_epi16( | ||||
|          _mm_setzero_si128(), _mm_setzero_si128()); | ||||
| #endif | ||||
| 
 | ||||
|    for (h = 0; h < height; h++, output += out_stride >> 2, input += in_stride) | ||||
|    { | ||||
|       const uint8_t *src = input; | ||||
|       uint32_t      *dst = output; | ||||
|       int              w = 0; | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
|       /* Each loop processes 16 pixels. */ | ||||
|       for (; w + 16 <= width; w += 16, src += 32, dst += 16) | ||||
|       { | ||||
|          __m128i u, v, u0_g, u1_g, u0_b, u1_b, v0_r, v1_r, v0_g, v1_g, | ||||
|                  r0, g0, b0, r1, g1, b1; | ||||
|          __m128i res_lo_bg, res_hi_bg, res_lo_ra, res_hi_ra; | ||||
|          __m128i res0, res1, res2, res3; | ||||
|          __m128i yuv0 = _mm_loadu_si128((const __m128i*)(src +  0)); /* [Y0, U0, Y1, V0, Y2, U1, Y3, V1, ...] */ | ||||
|          __m128i yuv1 = _mm_loadu_si128((const __m128i*)(src + 16)); /* [Y0, U0, Y1, V0, Y2, U1, Y3, V1, ...] */ | ||||
| 
 | ||||
|          __m128i _y0 = _mm_and_si128(yuv0, mask_y); /* [Y0, Y1, Y2, ...] (16-bit) */ | ||||
|          __m128i u0 = _mm_and_si128(yuv0, mask_u); /* [0, U0, 0, 0, 0, U1, 0, 0, ...] */ | ||||
|          __m128i v0 = _mm_and_si128(yuv0, mask_v); /* [0, 0, 0, V1, 0, , 0, V1, ...] */ | ||||
|          __m128i _y1 = _mm_and_si128(yuv1, mask_y); /* [Y0, Y1, Y2, ...] (16-bit) */ | ||||
|          __m128i u1 = _mm_and_si128(yuv1, mask_u); /* [0, U0, 0, 0, 0, U1, 0, 0, ...] */ | ||||
|          __m128i v1 = _mm_and_si128(yuv1, mask_v); /* [0, 0, 0, V1, 0, , 0, V1, ...] */ | ||||
| 
 | ||||
|          /* Juggle around to get U and V in the same 16-bit format as Y. */ | ||||
|          u0 = _mm_srli_si128(u0, 1); | ||||
|          v0 = _mm_srli_si128(v0, 3); | ||||
|          u1 = _mm_srli_si128(u1, 1); | ||||
|          v1 = _mm_srli_si128(v1, 3); | ||||
|          u = _mm_packs_epi32(u0, u1); | ||||
|          v = _mm_packs_epi32(v0, v1); | ||||
| 
 | ||||
|          /* Apply YUV offsets (U, V) -= (-128, -128). */ | ||||
|          u = _mm_sub_epi16(u, chroma_offset); | ||||
|          v = _mm_sub_epi16(v, chroma_offset); | ||||
| 
 | ||||
|          /* Upscale chroma horizontally (nearest). */ | ||||
|          u0 = _mm_unpacklo_epi16(u, u); | ||||
|          u1 = _mm_unpackhi_epi16(u, u); | ||||
|          v0 = _mm_unpacklo_epi16(v, v); | ||||
|          v1 = _mm_unpackhi_epi16(v, v); | ||||
| 
 | ||||
|          /* Apply transformations. */ | ||||
|          _y0 = _mm_mullo_epi16(_y0, yuv_mul); | ||||
|          _y1 = _mm_mullo_epi16(_y1, yuv_mul); | ||||
|          u0_g   = _mm_mullo_epi16(u0, u_g_mul); | ||||
|          u1_g   = _mm_mullo_epi16(u1, u_g_mul); | ||||
|          u0_b   = _mm_mullo_epi16(u0, u_b_mul); | ||||
|          u1_b   = _mm_mullo_epi16(u1, u_b_mul); | ||||
|          v0_r   = _mm_mullo_epi16(v0, v_r_mul); | ||||
|          v1_r   = _mm_mullo_epi16(v1, v_r_mul); | ||||
|          v0_g   = _mm_mullo_epi16(v0, v_g_mul); | ||||
|          v1_g   = _mm_mullo_epi16(v1, v_g_mul); | ||||
| 
 | ||||
|          /* Add contibutions from the transformed components. */ | ||||
|          r0 = _mm_srai_epi16(_mm_adds_epi16(_mm_adds_epi16(_y0, v0_r), | ||||
|                   round_offset), YUV_SHIFT); | ||||
|          g0 = _mm_srai_epi16(_mm_adds_epi16( | ||||
|                   _mm_adds_epi16(_mm_adds_epi16(_y0, v0_g), u0_g), round_offset), YUV_SHIFT); | ||||
|          b0 = _mm_srai_epi16(_mm_adds_epi16( | ||||
|                   _mm_adds_epi16(_y0, u0_b), round_offset), YUV_SHIFT); | ||||
| 
 | ||||
|          r1 = _mm_srai_epi16(_mm_adds_epi16( | ||||
|                   _mm_adds_epi16(_y1, v1_r), round_offset), YUV_SHIFT); | ||||
|          g1 = _mm_srai_epi16(_mm_adds_epi16( | ||||
|                   _mm_adds_epi16(_mm_adds_epi16(_y1, v1_g), u1_g), round_offset), YUV_SHIFT); | ||||
|          b1 = _mm_srai_epi16(_mm_adds_epi16( | ||||
|                   _mm_adds_epi16(_y1, u1_b), round_offset), YUV_SHIFT); | ||||
| 
 | ||||
|          /* Saturate into 8-bit. */ | ||||
|          r0 = _mm_packus_epi16(r0, r1); | ||||
|          g0 = _mm_packus_epi16(g0, g1); | ||||
|          b0 = _mm_packus_epi16(b0, b1); | ||||
| 
 | ||||
|          /* Interleave into ARGB. */ | ||||
|          res_lo_bg = _mm_unpacklo_epi8(b0, g0); | ||||
|          res_hi_bg = _mm_unpackhi_epi8(b0, g0); | ||||
|          res_lo_ra = _mm_unpacklo_epi8(r0, a); | ||||
|          res_hi_ra = _mm_unpackhi_epi8(r0, a); | ||||
|          res0 = _mm_unpacklo_epi16(res_lo_bg, res_lo_ra); | ||||
|          res1 = _mm_unpackhi_epi16(res_lo_bg, res_lo_ra); | ||||
|          res2 = _mm_unpacklo_epi16(res_hi_bg, res_hi_ra); | ||||
|          res3 = _mm_unpackhi_epi16(res_hi_bg, res_hi_ra); | ||||
| 
 | ||||
|          _mm_storeu_si128((__m128i*)(dst +  0), res0); | ||||
|          _mm_storeu_si128((__m128i*)(dst +  4), res1); | ||||
|          _mm_storeu_si128((__m128i*)(dst +  8), res2); | ||||
|          _mm_storeu_si128((__m128i*)(dst + 12), res3); | ||||
|       } | ||||
| #endif | ||||
| 
 | ||||
|       /* Finish off the rest (if any) in C. */ | ||||
|       for (; w < width; w += 2, src += 4, dst += 2) | ||||
|       { | ||||
|          int _y0    = src[0]; | ||||
|          int  u     = src[1] - 128; | ||||
|          int _y1    = src[2]; | ||||
|          int  v     = src[3] - 128; | ||||
| 
 | ||||
|          uint8_t r0 = clamp_8bit((YUV_MAT_Y * _y0 +                   YUV_MAT_V_R * v + YUV_OFFSET) >> YUV_SHIFT); | ||||
|          uint8_t g0 = clamp_8bit((YUV_MAT_Y * _y0 + YUV_MAT_U_G * u + YUV_MAT_V_G * v + YUV_OFFSET) >> YUV_SHIFT); | ||||
|          uint8_t b0 = clamp_8bit((YUV_MAT_Y * _y0 + YUV_MAT_U_B * u                   + YUV_OFFSET) >> YUV_SHIFT); | ||||
| 
 | ||||
|          uint8_t r1 = clamp_8bit((YUV_MAT_Y * _y1 +                   YUV_MAT_V_R * v + YUV_OFFSET) >> YUV_SHIFT); | ||||
|          uint8_t g1 = clamp_8bit((YUV_MAT_Y * _y1 + YUV_MAT_U_G * u + YUV_MAT_V_G * v + YUV_OFFSET) >> YUV_SHIFT); | ||||
|          uint8_t b1 = clamp_8bit((YUV_MAT_Y * _y1 + YUV_MAT_U_B * u                   + YUV_OFFSET) >> YUV_SHIFT); | ||||
| 
 | ||||
|          dst[0]     = 0xff000000u | (r0 << 16) | (g0 << 8) | (b0 << 0); | ||||
|          dst[1]     = 0xff000000u | (r1 << 16) | (g1 << 8) | (b1 << 0); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void conv_copy(void *output_, const void *input_, | ||||
|       int width, int height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h; | ||||
|    int copy_len         = abs(out_stride); | ||||
|    const uint8_t *input = (const uint8_t*)input_; | ||||
|    uint8_t *output      = (uint8_t*)output_; | ||||
| 
 | ||||
|    if (abs(in_stride) < copy_len) | ||||
|       copy_len          = abs(in_stride); | ||||
| 
 | ||||
|    for (h = 0; h < height; | ||||
|          h++, output += out_stride, input += in_stride) | ||||
|       memcpy(output, input, copy_len); | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,343 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (scaler.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <math.h> | ||||
| 
 | ||||
| #include <gfx/scaler/scaler.h> | ||||
| #include <gfx/scaler/scaler_int.h> | ||||
| #include <gfx/scaler/filter.h> | ||||
| #include <gfx/scaler/pixconv.h> | ||||
| 
 | ||||
| static bool allocate_frames(struct scaler_ctx *ctx) | ||||
| { | ||||
|    uint64_t *scaled_frame = NULL; | ||||
|    ctx->scaled.stride     = ((ctx->out_width + 7) & ~7) * sizeof(uint64_t); | ||||
|    ctx->scaled.width      = ctx->out_width; | ||||
|    ctx->scaled.height     = ctx->in_height; | ||||
|    scaled_frame           = (uint64_t*)calloc(sizeof(uint64_t), | ||||
|             (ctx->scaled.stride * ctx->scaled.height) >> 3); | ||||
| 
 | ||||
|    if (!scaled_frame) | ||||
|       return false; | ||||
| 
 | ||||
|    ctx->scaled.frame      = scaled_frame; | ||||
| 
 | ||||
|    if (ctx->in_fmt != SCALER_FMT_ARGB8888) | ||||
|    { | ||||
|       uint32_t *input_frame = NULL; | ||||
|       ctx->input.stride     = ((ctx->in_width + 7) & ~7) * sizeof(uint32_t); | ||||
|       input_frame           = (uint32_t*)calloc(sizeof(uint32_t), | ||||
|                (ctx->input.stride * ctx->in_height) >> 2); | ||||
| 
 | ||||
|       if (!input_frame) | ||||
|          return false; | ||||
| 
 | ||||
|       ctx->input.frame      = input_frame; | ||||
|    } | ||||
| 
 | ||||
|    if (ctx->out_fmt != SCALER_FMT_ARGB8888) | ||||
|    { | ||||
|       uint32_t *output_frame = NULL; | ||||
|       ctx->output.stride     = ((ctx->out_width + 7) & ~7) * sizeof(uint32_t); | ||||
| 
 | ||||
|       output_frame           = (uint32_t*)calloc(sizeof(uint32_t), | ||||
|                (ctx->output.stride * ctx->out_height) >> 2); | ||||
| 
 | ||||
|       if (!output_frame) | ||||
|          return false; | ||||
| 
 | ||||
|       ctx->output.frame      = output_frame; | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| bool scaler_ctx_gen_filter(struct scaler_ctx *ctx) | ||||
| { | ||||
|    scaler_ctx_gen_reset(ctx); | ||||
| 
 | ||||
|    ctx->scaler_special = NULL; | ||||
|    ctx->unscaled       = false; | ||||
| 
 | ||||
|    if (!allocate_frames(ctx)) | ||||
|       return false; | ||||
| 
 | ||||
|    if (     ctx->in_width  == ctx->out_width | ||||
|          && ctx->in_height == ctx->out_height) | ||||
|    { | ||||
|       ctx->unscaled     = true; /* Only pixel format conversion ... */ | ||||
| 
 | ||||
|       if (ctx->in_fmt == ctx->out_fmt) | ||||
|          ctx->direct_pixconv = conv_copy; | ||||
|       else | ||||
|       { | ||||
|          /* Bind a pixel converter callback function to the
 | ||||
|           * 'direct_pixconv' function pointer of the scaler context object. */ | ||||
|          switch (ctx->in_fmt) | ||||
|          { | ||||
|             case SCALER_FMT_0RGB1555: | ||||
|                switch (ctx->out_fmt) | ||||
|                { | ||||
|                   case SCALER_FMT_ARGB8888: | ||||
|                      ctx->direct_pixconv = conv_0rgb1555_argb8888; | ||||
|                      break; | ||||
|                   case SCALER_FMT_RGB565: | ||||
|                      ctx->direct_pixconv = conv_0rgb1555_rgb565; | ||||
|                      break; | ||||
|                   case SCALER_FMT_BGR24: | ||||
|                      ctx->direct_pixconv = conv_0rgb1555_bgr24; | ||||
|                      break; | ||||
|                   default: | ||||
|                      break; | ||||
|                } | ||||
|                break; | ||||
|             case SCALER_FMT_RGB565: | ||||
|                switch (ctx->out_fmt) | ||||
|                { | ||||
|                   case SCALER_FMT_ARGB8888: | ||||
|                      ctx->direct_pixconv = conv_rgb565_argb8888; | ||||
|                      break; | ||||
|                   case SCALER_FMT_BGR24: | ||||
|                      ctx->direct_pixconv = conv_rgb565_bgr24; | ||||
|                      break; | ||||
|                   case SCALER_FMT_0RGB1555: | ||||
|                      ctx->direct_pixconv = conv_rgb565_0rgb1555; | ||||
|                      break; | ||||
|                   default: | ||||
|                      break; | ||||
|                } | ||||
|                break; | ||||
|             case SCALER_FMT_BGR24: | ||||
|                switch (ctx->out_fmt) | ||||
|                { | ||||
|                   case SCALER_FMT_ARGB8888: | ||||
|                      ctx->direct_pixconv = conv_bgr24_argb8888; | ||||
|                      break; | ||||
|                   default: | ||||
|                      break; | ||||
|                } | ||||
|                break; | ||||
|             case SCALER_FMT_ARGB8888: | ||||
|                switch (ctx->out_fmt) | ||||
|                { | ||||
|                   case SCALER_FMT_0RGB1555: | ||||
|                      ctx->direct_pixconv = conv_argb8888_0rgb1555; | ||||
|                      break; | ||||
|                   case SCALER_FMT_BGR24: | ||||
|                      ctx->direct_pixconv = conv_argb8888_bgr24; | ||||
|                      break; | ||||
|                   case SCALER_FMT_ABGR8888: | ||||
|                      ctx->direct_pixconv = conv_argb8888_abgr8888; | ||||
|                      break; | ||||
|                   case SCALER_FMT_RGBA4444: | ||||
|                      ctx->direct_pixconv = conv_argb8888_rgba4444; | ||||
|                      break; | ||||
|                   default: | ||||
|                      break; | ||||
|                } | ||||
|                break; | ||||
|             case SCALER_FMT_YUYV: | ||||
|                switch (ctx->out_fmt) | ||||
|                { | ||||
|                   case SCALER_FMT_ARGB8888: | ||||
|                      ctx->direct_pixconv = conv_yuyv_argb8888; | ||||
|                      break; | ||||
|                   default: | ||||
|                      break; | ||||
|                } | ||||
|                break; | ||||
|             case SCALER_FMT_RGBA4444: | ||||
|                switch (ctx->out_fmt) | ||||
|                { | ||||
|                   case SCALER_FMT_ARGB8888: | ||||
|                      ctx->direct_pixconv = conv_rgba4444_argb8888; | ||||
|                      break; | ||||
|                   case SCALER_FMT_RGB565: | ||||
|                      ctx->direct_pixconv = conv_rgba4444_rgb565; | ||||
|                      break; | ||||
|                   default: | ||||
|                      break; | ||||
|                } | ||||
|                break; | ||||
|             case SCALER_FMT_ABGR8888: | ||||
|                /* FIXME/TODO */ | ||||
|                break; | ||||
|          } | ||||
| 
 | ||||
|          if (!ctx->direct_pixconv) | ||||
|             return false; | ||||
|       } | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       ctx->scaler_horiz = scaler_argb8888_horiz; | ||||
|       ctx->scaler_vert  = scaler_argb8888_vert; | ||||
| 
 | ||||
|       switch (ctx->in_fmt) | ||||
|       { | ||||
|          case SCALER_FMT_ARGB8888: | ||||
|             /* No need to convert :D */ | ||||
|             break; | ||||
| 
 | ||||
|          case SCALER_FMT_0RGB1555: | ||||
|             ctx->in_pixconv = conv_0rgb1555_argb8888; | ||||
|             break; | ||||
| 
 | ||||
|          case SCALER_FMT_RGB565: | ||||
|             ctx->in_pixconv = conv_rgb565_argb8888; | ||||
|             break; | ||||
| 
 | ||||
|          case SCALER_FMT_BGR24: | ||||
|             ctx->in_pixconv = conv_bgr24_argb8888; | ||||
|             break; | ||||
| 
 | ||||
|          case SCALER_FMT_RGBA4444: | ||||
|             ctx->in_pixconv = conv_rgba4444_argb8888; | ||||
|             break; | ||||
| 
 | ||||
|          default: | ||||
|             return false; | ||||
|       } | ||||
| 
 | ||||
|       switch (ctx->out_fmt) | ||||
|       { | ||||
|          case SCALER_FMT_ARGB8888: | ||||
|             /* No need to convert :D */ | ||||
|             break; | ||||
| 
 | ||||
|          case SCALER_FMT_RGBA4444: | ||||
|             ctx->out_pixconv = conv_argb8888_rgba4444; | ||||
|             break; | ||||
| 
 | ||||
|          case SCALER_FMT_0RGB1555: | ||||
|             ctx->out_pixconv = conv_argb8888_0rgb1555; | ||||
|             break; | ||||
| 
 | ||||
|          case SCALER_FMT_BGR24: | ||||
|             ctx->out_pixconv = conv_argb8888_bgr24; | ||||
|             break; | ||||
| 
 | ||||
|          default: | ||||
|             return false; | ||||
|       } | ||||
| 
 | ||||
|       if (!scaler_gen_filter(ctx)) | ||||
|          return false; | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| void scaler_ctx_gen_reset(struct scaler_ctx *ctx) | ||||
| { | ||||
|    if (ctx->horiz.filter) | ||||
|       free(ctx->horiz.filter); | ||||
|    if (ctx->horiz.filter_pos) | ||||
|       free(ctx->horiz.filter_pos); | ||||
|    if (ctx->vert.filter) | ||||
|       free(ctx->vert.filter); | ||||
|    if (ctx->vert.filter_pos) | ||||
|       free(ctx->vert.filter_pos); | ||||
|    if (ctx->scaled.frame) | ||||
|       free(ctx->scaled.frame); | ||||
|    if (ctx->input.frame) | ||||
|       free(ctx->input.frame); | ||||
|    if (ctx->output.frame) | ||||
|       free(ctx->output.frame); | ||||
| 
 | ||||
|    ctx->horiz.filter        = NULL; | ||||
|    ctx->horiz.filter_len    = 0; | ||||
|    ctx->horiz.filter_stride = 0; | ||||
|    ctx->horiz.filter_pos    = NULL; | ||||
| 
 | ||||
|    ctx->vert.filter         = NULL; | ||||
|    ctx->vert.filter_len     = 0; | ||||
|    ctx->vert.filter_stride  = 0; | ||||
|    ctx->vert.filter_pos     = NULL; | ||||
| 
 | ||||
|    ctx->scaled.frame        = NULL; | ||||
|    ctx->scaled.width        = 0; | ||||
|    ctx->scaled.height       = 0; | ||||
|    ctx->scaled.stride       = 0; | ||||
| 
 | ||||
|    ctx->input.frame         = NULL; | ||||
|    ctx->input.stride        = 0; | ||||
| 
 | ||||
|    ctx->output.frame        = NULL; | ||||
|    ctx->output.stride       = 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * scaler_ctx_scale: | ||||
|  * @ctx          : pointer to scaler context object. | ||||
|  * @output       : pointer to output image. | ||||
|  * @input        : pointer to input image. | ||||
|  * | ||||
|  * Scales an input image to an output image. | ||||
|  **/ | ||||
| void scaler_ctx_scale(struct scaler_ctx *ctx, | ||||
|       void *output, const void *input) | ||||
| { | ||||
|    const void *input_frame = input; | ||||
|    void *output_frame      = output; | ||||
|    int input_stride        = ctx->in_stride; | ||||
|    int output_stride       = ctx->out_stride; | ||||
| 
 | ||||
|    if (ctx->in_fmt != SCALER_FMT_ARGB8888) | ||||
|    { | ||||
|       ctx->in_pixconv(ctx->input.frame, input, | ||||
|             ctx->in_width, ctx->in_height, | ||||
|             ctx->input.stride, ctx->in_stride); | ||||
| 
 | ||||
|       input_frame       = ctx->input.frame; | ||||
|       input_stride      = ctx->input.stride; | ||||
|    } | ||||
| 
 | ||||
|    if (ctx->out_fmt != SCALER_FMT_ARGB8888) | ||||
|    { | ||||
|       output_frame  = ctx->output.frame; | ||||
|       output_stride = ctx->output.stride; | ||||
|    } | ||||
| 
 | ||||
|    /* Take some special, and (hopefully) more optimized path. */ | ||||
|    if (ctx->scaler_special) | ||||
|       ctx->scaler_special(ctx, output_frame, input_frame, | ||||
|             ctx->out_width, ctx->out_height, | ||||
|             ctx->in_width, ctx->in_height, | ||||
|             output_stride, input_stride); | ||||
|    else | ||||
|    { | ||||
|       /* Take generic filter path. */ | ||||
|       if (ctx->scaler_horiz) | ||||
|          ctx->scaler_horiz(ctx, input_frame, input_stride); | ||||
|       if (ctx->scaler_vert) | ||||
|          ctx->scaler_vert (ctx, output, output_stride); | ||||
|    } | ||||
| 
 | ||||
|    if (ctx->out_fmt != SCALER_FMT_ARGB8888) | ||||
|       ctx->out_pixconv(output, ctx->output.frame, | ||||
|             ctx->out_width, ctx->out_height, | ||||
|             ctx->out_stride, ctx->output.stride); | ||||
| } | ||||
|  | @ -0,0 +1,243 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (scaler_filter.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <gfx/scaler/filter.h> | ||||
| #include <gfx/scaler/scaler_int.h> | ||||
| #include <retro_inline.h> | ||||
| #include <filters.h> | ||||
| #include <retro_math.h> | ||||
| 
 | ||||
| #define FILTER_UNITY (1 << 14) | ||||
| 
 | ||||
| static INLINE void gen_filter_point_sub(struct scaler_filter *filter, | ||||
|       int len, int pos, int step) | ||||
| { | ||||
|    int i; | ||||
|    for (i = 0; i < len; i++, pos += step) | ||||
|    { | ||||
|       filter->filter_pos[i] = pos >> 16; | ||||
|       filter->filter[i]     = FILTER_UNITY; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static INLINE void gen_filter_bilinear_sub(struct scaler_filter *filter, | ||||
|       int len, int pos, int step) | ||||
| { | ||||
|    int i; | ||||
|    for (i = 0; i < len; i++, pos += step) | ||||
|    { | ||||
|       filter->filter_pos[i]     = pos >> 16; | ||||
|       filter->filter[i * 2 + 1] = (pos & 0xffff) >> 2; | ||||
|       filter->filter[i * 2 + 0] = FILTER_UNITY - filter->filter[i * 2 + 1]; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static INLINE void gen_filter_sinc_sub(struct scaler_filter *filter, | ||||
|       int len, int pos, int step, double phase_mul) | ||||
| { | ||||
|    int i, j; | ||||
|    const int sinc_size = filter->filter_len; | ||||
| 
 | ||||
|    for (i = 0; i < len; i++, pos += step) | ||||
|    { | ||||
|       filter->filter_pos[i] = pos >> 16; | ||||
| 
 | ||||
|       for (j = 0; j < sinc_size; j++) | ||||
|       { | ||||
|          double sinc_phase    = M_PI * ((double)((sinc_size << 15) + (pos & 0xffff)) / 0x10000 - j); | ||||
|          double lanczos_phase = sinc_phase / ((sinc_size >> 1)); | ||||
|          int16_t sinc_val     = FILTER_UNITY * sinc(sinc_phase * phase_mul) * sinc(lanczos_phase) * phase_mul; | ||||
| 
 | ||||
|          filter->filter[i * sinc_size + j] = sinc_val; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static bool validate_filter(struct scaler_ctx *ctx) | ||||
| { | ||||
|    int i; | ||||
|    int max_h_pos; | ||||
|    int max_w_pos = ctx->in_width - ctx->horiz.filter_len; | ||||
| 
 | ||||
|    for (i = 0; i < ctx->out_width; i++) | ||||
|    { | ||||
|       if (ctx->horiz.filter_pos[i] > max_w_pos || ctx->horiz.filter_pos[i] < 0) | ||||
|       { | ||||
|          fprintf(stderr, "Out X = %d => In X = %d\n", i, ctx->horiz.filter_pos[i]); | ||||
|          return false; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    max_h_pos = ctx->in_height - ctx->vert.filter_len; | ||||
| 
 | ||||
|    for (i = 0; i < ctx->out_height; i++) | ||||
|    { | ||||
|       if (ctx->vert.filter_pos[i] > max_h_pos || ctx->vert.filter_pos[i] < 0) | ||||
|       { | ||||
|          fprintf(stderr, "Out Y = %d => In Y = %d\n", i, ctx->vert.filter_pos[i]); | ||||
|          return false; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| static void fixup_filter_sub(struct scaler_filter *filter, | ||||
|       int out_len, int in_len) | ||||
| { | ||||
|    int i; | ||||
|    int max_pos = in_len - filter->filter_len; | ||||
| 
 | ||||
|    for (i = 0; i < out_len; i++) | ||||
|    { | ||||
|       int postsample =  filter->filter_pos[i] - max_pos; | ||||
|       int presample  = -filter->filter_pos[i]; | ||||
| 
 | ||||
|       if (postsample > 0) | ||||
|       { | ||||
|          int16_t *base_filter   = NULL; | ||||
| 
 | ||||
|          filter->filter_pos[i] -= postsample; | ||||
| 
 | ||||
|          base_filter            = filter->filter + i * filter->filter_stride; | ||||
| 
 | ||||
|          if (postsample > (int)filter->filter_len) | ||||
|             memset(base_filter, 0, filter->filter_len * sizeof(int16_t)); | ||||
|          else | ||||
|          { | ||||
|             memmove(base_filter + postsample, base_filter, | ||||
|                   (filter->filter_len - postsample) * sizeof(int16_t)); | ||||
|             memset(base_filter, 0, postsample * sizeof(int16_t)); | ||||
|          } | ||||
|       } | ||||
| 
 | ||||
|       if (presample > 0) | ||||
|       { | ||||
|          int16_t *base_filter   = NULL; | ||||
| 
 | ||||
|          filter->filter_pos[i] += presample; | ||||
|          base_filter            = filter->filter + i * filter->filter_stride; | ||||
| 
 | ||||
|          if (presample > (int)filter->filter_len) | ||||
|             memset(base_filter, 0, filter->filter_len * sizeof(int16_t)); | ||||
|          else | ||||
|          { | ||||
|             memmove(base_filter, base_filter + presample, | ||||
|                   (filter->filter_len - presample) * sizeof(int16_t)); | ||||
|             memset(base_filter + (filter->filter_len - presample), | ||||
|                   0, presample * sizeof(int16_t)); | ||||
|          } | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| bool scaler_gen_filter(struct scaler_ctx *ctx) | ||||
| { | ||||
|    int x_pos, x_step, y_pos, y_step; | ||||
|    int sinc_size = 0; | ||||
| 
 | ||||
|    switch (ctx->scaler_type) | ||||
|    { | ||||
|       case SCALER_TYPE_POINT: | ||||
|          ctx->horiz.filter_len    = 1; | ||||
|          ctx->horiz.filter_stride = 1; | ||||
|          ctx->vert.filter_len     = 1; | ||||
|          ctx->vert.filter_stride  = 1; | ||||
|          break; | ||||
|       case SCALER_TYPE_BILINEAR: | ||||
|          ctx->horiz.filter_len    = 2; | ||||
|          ctx->horiz.filter_stride = 2; | ||||
|          ctx->vert.filter_len     = 2; | ||||
|          ctx->vert.filter_stride  = 2; | ||||
|          break; | ||||
|       case SCALER_TYPE_SINC: | ||||
|          sinc_size                = 8 * ((ctx->in_width > ctx->out_width) | ||||
|                ? next_pow2(ctx->in_width / ctx->out_width) : 1); | ||||
|          ctx->horiz.filter_len    = sinc_size; | ||||
|          ctx->horiz.filter_stride = sinc_size; | ||||
|          ctx->vert.filter_len     = sinc_size; | ||||
|          ctx->vert.filter_stride  = sinc_size; | ||||
|          break; | ||||
|       case SCALER_TYPE_UNKNOWN: | ||||
|       default: | ||||
|          return false; | ||||
|    } | ||||
| 
 | ||||
|    ctx->horiz.filter     = (int16_t*)calloc(sizeof(int16_t), ctx->horiz.filter_stride * ctx->out_width); | ||||
|    ctx->horiz.filter_pos = (int*)calloc(sizeof(int), ctx->out_width); | ||||
| 
 | ||||
|    ctx->vert.filter      = (int16_t*)calloc(sizeof(int16_t), ctx->vert.filter_stride * ctx->out_height); | ||||
|    ctx->vert.filter_pos  = (int*)calloc(sizeof(int), ctx->out_height); | ||||
| 
 | ||||
|    if (!ctx->horiz.filter || !ctx->vert.filter) | ||||
|       return false; | ||||
| 
 | ||||
|    x_step = (1 << 16) * ctx->in_width / ctx->out_width; | ||||
|    y_step = (1 << 16) * ctx->in_height / ctx->out_height; | ||||
| 
 | ||||
|    switch (ctx->scaler_type) | ||||
|    { | ||||
|       case SCALER_TYPE_POINT: | ||||
|          x_pos  = (1 << 15) * ctx->in_width / ctx->out_width   - (1 << 15); | ||||
|          y_pos  = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15); | ||||
| 
 | ||||
|          gen_filter_point_sub(&ctx->horiz, ctx->out_width,  x_pos, x_step); | ||||
|          gen_filter_point_sub(&ctx->vert,  ctx->out_height, y_pos, y_step); | ||||
| 
 | ||||
|          ctx->scaler_special = scaler_argb8888_point_special; | ||||
|          break; | ||||
| 
 | ||||
|       case SCALER_TYPE_BILINEAR: | ||||
|          x_pos  = (1 << 15) * ctx->in_width / ctx->out_width   - (1 << 15); | ||||
|          y_pos  = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15); | ||||
| 
 | ||||
|          gen_filter_bilinear_sub(&ctx->horiz, ctx->out_width,  x_pos, x_step); | ||||
|          gen_filter_bilinear_sub(&ctx->vert,  ctx->out_height, y_pos, y_step); | ||||
|          break; | ||||
| 
 | ||||
|       case SCALER_TYPE_SINC: | ||||
|          /* Need to expand the filter when downsampling
 | ||||
|           * to get a proper low-pass effect. */ | ||||
| 
 | ||||
|          x_pos  = (1 << 15) * ctx->in_width  / ctx->out_width  - (1 << 15) - (sinc_size << 15); | ||||
|          y_pos  = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15) - (sinc_size << 15); | ||||
| 
 | ||||
|          gen_filter_sinc_sub(&ctx->horiz, ctx->out_width, x_pos, x_step, | ||||
|                ctx->in_width  > ctx->out_width  ? (double)ctx->out_width  / ctx->in_width  : 1.0); | ||||
|          gen_filter_sinc_sub(&ctx->vert, ctx->out_height, y_pos, y_step, | ||||
|                ctx->in_height > ctx->out_height ? (double)ctx->out_height / ctx->in_height : 1.0 | ||||
|                ); | ||||
|          break; | ||||
|       case SCALER_TYPE_UNKNOWN: | ||||
|          break; | ||||
|    } | ||||
| 
 | ||||
|    /* Makes sure that we never sample outside our rectangle. */ | ||||
|    fixup_filter_sub(&ctx->horiz, ctx->out_width, ctx->in_width); | ||||
|    fixup_filter_sub(&ctx->vert,  ctx->out_height, ctx->in_height); | ||||
| 
 | ||||
|    return validate_filter(ctx); | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,261 @@ | |||
| /* Copyright  (C) 2010-2017 The RetroArch team
 | ||||
|  * | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * The following license statement only applies to this file (scaler_int.c). | ||||
|  * --------------------------------------------------------------------------------------- | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, | ||||
|  * to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <gfx/scaler/scaler_int.h> | ||||
| 
 | ||||
| #include <retro_inline.h> | ||||
| 
 | ||||
| #ifdef SCALER_NO_SIMD | ||||
| #undef __SSE2__ | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__SSE2__) | ||||
| #include <emmintrin.h> | ||||
| #ifdef _WIN32 | ||||
| #include <intrin.h> | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| /* ARGB8888 scaler is split in two:
 | ||||
|  * | ||||
|  * First, horizontal scaler is applied. | ||||
|  * Here, all 8-bit channels are expanded to 16-bit. Values are then shifted 7 | ||||
|  * to left to occupy 15 bits. | ||||
|  * | ||||
|  * The sign bit is kept empty as we have to do signed multiplication for the | ||||
|  * filter. | ||||
|  * | ||||
|  * A mulhi [(a * b) >> 16] is applied which loses some precision, but is | ||||
|  * very efficient for SIMD. | ||||
|  * It is accurate enough for 8-bit purposes. | ||||
|  * | ||||
|  * The fixed point 1.0 for filter is (1 << 14). After horizontal scale, | ||||
|  * the output is kept with 16-bit channels, and will now have 13 bits | ||||
|  * of precision as [(a * (1 << 14)) >> 16] is effectively a right shift by 2. | ||||
|  * | ||||
|  * Vertical scaler takes the 13 bit channels, and performs the | ||||
|  * same mulhi steps. | ||||
|  * Another 2 bits of precision is lost, which ends up as 11 bits. | ||||
|  * Scaling is now complete. Channels are shifted right by 3, and saturated | ||||
|  * into 8-bit values. | ||||
|  * | ||||
|  * The C version of scalers perform the exact same operations as the | ||||
|  * SIMD code for testing purposes. | ||||
|  */ | ||||
| 
 | ||||
| void scaler_argb8888_vert(const struct scaler_ctx *ctx, void *output_, int stride) | ||||
| { | ||||
|    int h, w, y; | ||||
|    const uint64_t      *input = ctx->scaled.frame; | ||||
|    uint32_t           *output = (uint32_t*)output_; | ||||
| 
 | ||||
|    const int16_t *filter_vert = ctx->vert.filter; | ||||
| 
 | ||||
|    for (h = 0; h < ctx->out_height; h++, | ||||
|          filter_vert += ctx->vert.filter_stride, output += stride >> 2) | ||||
|    { | ||||
|       const uint64_t *input_base = input + ctx->vert.filter_pos[h] | ||||
|          * (ctx->scaled.stride >> 3); | ||||
| 
 | ||||
|       for (w = 0; w < ctx->out_width; w++) | ||||
|       { | ||||
|          const uint64_t *input_base_y = input_base + w; | ||||
| #if defined(__SSE2__) | ||||
|          __m128i final; | ||||
|          __m128i res = _mm_setzero_si128(); | ||||
| 
 | ||||
|          for (y = 0; (y + 1) < ctx->vert.filter_len; y += 2, | ||||
|                input_base_y += (ctx->scaled.stride >> 2)) | ||||
|          { | ||||
|             __m128i coeff = _mm_set_epi64x(filter_vert[y + 1] * 0x0001000100010001ll, filter_vert[y + 0] * 0x0001000100010001ll); | ||||
|             __m128i col   = _mm_set_epi64x(input_base_y[ctx->scaled.stride >> 3], input_base_y[0]); | ||||
| 
 | ||||
|             res           = _mm_adds_epi16(_mm_mulhi_epi16(col, coeff), res); | ||||
|          } | ||||
| 
 | ||||
|          for (; y < ctx->vert.filter_len; y++, input_base_y += (ctx->scaled.stride >> 3)) | ||||
|          { | ||||
|             __m128i coeff = _mm_set_epi64x(0, filter_vert[y] * 0x0001000100010001ll); | ||||
|             __m128i col   = _mm_set_epi64x(0, input_base_y[0]); | ||||
| 
 | ||||
|             res           = _mm_adds_epi16(_mm_mulhi_epi16(col, coeff), res); | ||||
|          } | ||||
| 
 | ||||
|          res       = _mm_adds_epi16(_mm_srli_si128(res, 8), res); | ||||
|          res       = _mm_srai_epi16(res, (7 - 2 - 2)); | ||||
| 
 | ||||
|          final     = _mm_packus_epi16(res, res); | ||||
| 
 | ||||
|          output[w] = _mm_cvtsi128_si32(final); | ||||
| #else | ||||
|          int16_t res_a = 0; | ||||
|          int16_t res_r = 0; | ||||
|          int16_t res_g = 0; | ||||
|          int16_t res_b = 0; | ||||
| 
 | ||||
|          for (y = 0; y < ctx->vert.filter_len; y++, | ||||
|                input_base_y += (ctx->scaled.stride >> 3)) | ||||
|          { | ||||
|             uint64_t col   = *input_base_y; | ||||
| 
 | ||||
|             int16_t a      = (col >> 48) & 0xffff; | ||||
|             int16_t r      = (col >> 32) & 0xffff; | ||||
|             int16_t g      = (col >> 16) & 0xffff; | ||||
|             int16_t b      = (col >>  0) & 0xffff; | ||||
| 
 | ||||
|             int16_t coeff  = filter_vert[y]; | ||||
| 
 | ||||
|             res_a         += (a * coeff) >> 16; | ||||
|             res_r         += (r * coeff) >> 16; | ||||
|             res_g         += (g * coeff) >> 16; | ||||
|             res_b         += (b * coeff) >> 16; | ||||
|          } | ||||
| 
 | ||||
|          res_a           >>= (7 - 2 - 2); | ||||
|          res_r           >>= (7 - 2 - 2); | ||||
|          res_g           >>= (7 - 2 - 2); | ||||
|          res_b           >>= (7 - 2 - 2); | ||||
| 
 | ||||
|          output[w]         = | ||||
|             (clamp_8bit(res_a) << 24) | | ||||
|             (clamp_8bit(res_r) << 16) | | ||||
|             (clamp_8bit(res_g) << 8)  | | ||||
|             (clamp_8bit(res_b) << 0); | ||||
| #endif | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void scaler_argb8888_horiz(const struct scaler_ctx *ctx, const void *input_, int stride) | ||||
| { | ||||
|    int h, w, x; | ||||
|    const uint32_t *input = (uint32_t*)input_; | ||||
|    uint64_t *output      = ctx->scaled.frame; | ||||
| 
 | ||||
|    for (h = 0; h < ctx->scaled.height; h++, input += stride >> 2, | ||||
|          output += ctx->scaled.stride >> 3) | ||||
|    { | ||||
|       const int16_t *filter_horiz = ctx->horiz.filter; | ||||
| 
 | ||||
|       for (w = 0; w < ctx->scaled.width; w++, | ||||
|             filter_horiz += ctx->horiz.filter_stride) | ||||
|       { | ||||
|          const uint32_t *input_base_x = input + ctx->horiz.filter_pos[w]; | ||||
| #if defined(__SSE2__) | ||||
|          __m128i res = _mm_setzero_si128(); | ||||
| 
 | ||||
|          for (x = 0; (x + 1) < ctx->horiz.filter_len; x += 2) | ||||
|          { | ||||
|             __m128i coeff = _mm_set_epi64x(filter_horiz[x + 1] * 0x0001000100010001ll, filter_horiz[x + 0] * 0x0001000100010001ll); | ||||
| 
 | ||||
|             __m128i col   = _mm_unpacklo_epi8(_mm_set_epi64x(0, | ||||
|                      ((uint64_t)input_base_x[x + 1] << 32) | input_base_x[x + 0]), _mm_setzero_si128()); | ||||
| 
 | ||||
|             col           = _mm_slli_epi16(col, 7); | ||||
|             res           = _mm_adds_epi16(_mm_mulhi_epi16(col, coeff), res); | ||||
|          } | ||||
| 
 | ||||
|          for (; x < ctx->horiz.filter_len; x++) | ||||
|          { | ||||
|             __m128i coeff = _mm_set_epi64x(0, filter_horiz[x] * 0x0001000100010001ll); | ||||
|             __m128i col   = _mm_unpacklo_epi8(_mm_set_epi32(0, 0, 0, input_base_x[x]), _mm_setzero_si128()); | ||||
| 
 | ||||
|             col           = _mm_slli_epi16(col, 7); | ||||
|             res           = _mm_adds_epi16(_mm_mulhi_epi16(col, coeff), res); | ||||
|          } | ||||
| 
 | ||||
|          res              = _mm_adds_epi16(_mm_srli_si128(res, 8), res); | ||||
| 
 | ||||
| #ifdef __x86_64__ | ||||
|          output[w]        = _mm_cvtsi128_si64(res); | ||||
| #else /* 32-bit doesn't have si64. Do it in two steps. */ | ||||
|          union | ||||
|          { | ||||
|             uint32_t *u32; | ||||
|             uint64_t *u64; | ||||
|          } u; | ||||
|          u.u64    = output + w; | ||||
|          u.u32[0] = _mm_cvtsi128_si32(res); | ||||
|          u.u32[1] = _mm_cvtsi128_si32(_mm_srli_si128(res, 4)); | ||||
| #endif | ||||
| #else | ||||
|          int16_t res_a = 0; | ||||
|          int16_t res_r = 0; | ||||
|          int16_t res_g = 0; | ||||
|          int16_t res_b = 0; | ||||
| 
 | ||||
|          for (x = 0; x < ctx->horiz.filter_len; x++) | ||||
|          { | ||||
|             uint32_t col   = input_base_x[x]; | ||||
| 
 | ||||
|             int16_t a      = (col >> (24 - 7)) & (0xff << 7); | ||||
|             int16_t r      = (col >> (16 - 7)) & (0xff << 7); | ||||
|             int16_t g      = (col >> ( 8 - 7)) & (0xff << 7); | ||||
|             int16_t b      = (col << ( 0 + 7)) & (0xff << 7); | ||||
| 
 | ||||
|             int16_t coeff  = filter_horiz[x]; | ||||
| 
 | ||||
|             res_a         += (a * coeff) >> 16; | ||||
|             res_r         += (r * coeff) >> 16; | ||||
|             res_g         += (g * coeff) >> 16; | ||||
|             res_b         += (b * coeff) >> 16; | ||||
|          } | ||||
| 
 | ||||
|          output[w]         = ( | ||||
|                (uint64_t)res_a  << 48)  | | ||||
|                ((uint64_t)res_r << 32)  | | ||||
|                ((uint64_t)res_g << 16)  | | ||||
|                ((uint64_t)res_b << 0); | ||||
| #endif | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void scaler_argb8888_point_special(const struct scaler_ctx *ctx, | ||||
|       void *output_, const void *input_, | ||||
|       int out_width, int out_height, | ||||
|       int in_width, int in_height, | ||||
|       int out_stride, int in_stride) | ||||
| { | ||||
|    int h, w; | ||||
|    int x_pos             = (1 << 15) * in_width / out_width - (1 << 15); | ||||
|    int x_step            = (1 << 16) * in_width / out_width; | ||||
|    int y_pos             = (1 << 15) * in_height / out_height - (1 << 15); | ||||
|    int y_step            = (1 << 16) * in_height / out_height; | ||||
|    const uint32_t *input = (const uint32_t*)input_; | ||||
|    uint32_t *output      = (uint32_t*)output_; | ||||
| 
 | ||||
|    if (x_pos < 0) | ||||
|       x_pos = 0; | ||||
|    if (y_pos < 0) | ||||
|       y_pos = 0; | ||||
| 
 | ||||
|    for (h = 0; h < out_height; h++, y_pos += y_step, output += out_stride >> 2) | ||||
|    { | ||||
|       int               x = x_pos; | ||||
|       const uint32_t *inp = input + (y_pos >> 16) * (in_stride >> 2); | ||||
| 
 | ||||
|       for (w = 0; w < out_width; w++, x += x_step) | ||||
|          output[w] = inp[x >> 16]; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,12 @@ | |||
| # Autogenerate GL extension loaders | ||||
| 
 | ||||
| ## OpenGL desktop | ||||
| 
 | ||||
| Use Khronos' recent [header](www.opengl.org/registry/api/glext.h). | ||||
| 
 | ||||
|     ./glgen.py /usr/include/GL/glext.h glsym_gl.h glsym_gl.c | ||||
| 
 | ||||
| ## OpenGL ES | ||||
| 
 | ||||
|     ./glgen.py /usr/include/GLES2/gl2ext.h glsym_es2.h glsym_es2.c | ||||
| 
 | ||||
|  | @ -0,0 +1,132 @@ | |||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| """ | ||||
|    License statement applies to this file (glgen.py) only. | ||||
| """  | ||||
| 
 | ||||
| """ | ||||
|    Permission is hereby granted, free of charge, | ||||
|    to any person obtaining a copy of this software and associated documentation files (the "Software"), | ||||
|    to deal in the Software without restriction, including without limitation the rights to | ||||
|    use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | ||||
|    and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
| 
 | ||||
|    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
| 
 | ||||
|    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | ||||
|    INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
|    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
| """ | ||||
| 
 | ||||
| import sys | ||||
| import os | ||||
| import re | ||||
| 
 | ||||
| banned_ext = [ 'AMD', 'APPLE', 'NV', 'NVX', 'ATI', '3DLABS', 'SUN', 'SGI', 'SGIX', 'SGIS', 'INTEL', '3DFX', 'IBM', 'MESA', 'GREMEDY', 'OML', 'PGI', 'I3D', 'INGL', 'MTX', 'QCOM', 'IMG', 'ANGLE', 'SUNX', 'INGR' ] | ||||
| 
 | ||||
| def noext(sym): | ||||
|    for ext in banned_ext: | ||||
|       if sym.endswith(ext): | ||||
|          return False | ||||
|    return True | ||||
| 
 | ||||
| def find_gl_symbols(lines): | ||||
|    typedefs = [] | ||||
|    syms = [] | ||||
|    for line in lines: | ||||
|       m = re.search(r'^typedef.+PFN(\S+)PROC.+$', line) | ||||
|       g = re.search(r'^.+(gl\S+)\W*\(.+\).*$', line) | ||||
|       if m and noext(m.group(1)): | ||||
|          typedefs.append(m.group(0).replace('PFN', 'RGLSYM').replace('GLDEBUGPROC', 'RGLGENGLDEBUGPROC')) | ||||
|       if g and noext(g.group(1)): | ||||
|          syms.append(g.group(1)) | ||||
|    return (typedefs, syms) | ||||
| 
 | ||||
| def generate_defines(gl_syms): | ||||
|    res = [] | ||||
|    for line in gl_syms: | ||||
|       res.append('#define {} __rglgen_{}'.format(line, line)) | ||||
|    return res | ||||
| 
 | ||||
| def generate_declarations(gl_syms): | ||||
|    return ['RGLSYM' + x.upper() + 'PROC ' + '__rglgen_' + x + ';' for x in gl_syms] | ||||
| 
 | ||||
| def generate_macros(gl_syms): | ||||
|    return ['    SYM(' + x.replace('gl', '') + '),' for x in gl_syms] | ||||
| 
 | ||||
| def dump(f, lines): | ||||
|    f.write('\n'.join(lines)) | ||||
|    f.write('\n\n') | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
| 
 | ||||
|    if len(sys.argv) > 4: | ||||
|       for banned in sys.argv[4:]: | ||||
|          banned_ext.append(banned) | ||||
| 
 | ||||
|    with open(sys.argv[1], 'r') as f: | ||||
|       lines = f.readlines() | ||||
|       typedefs, syms = find_gl_symbols(lines) | ||||
| 
 | ||||
|       overrides = generate_defines(syms) | ||||
|       declarations = generate_declarations(syms) | ||||
|       externs = ['extern ' + x for x in declarations] | ||||
| 
 | ||||
|       macros = generate_macros(syms) | ||||
| 
 | ||||
|    with open(sys.argv[2], 'w') as f: | ||||
|       f.write('#ifndef RGLGEN_DECL_H__\n') | ||||
|       f.write('#define RGLGEN_DECL_H__\n') | ||||
| 
 | ||||
|       f.write('#ifdef __cplusplus\n') | ||||
|       f.write('extern "C" {\n') | ||||
|       f.write('#endif\n') | ||||
| 
 | ||||
|       f.write('#ifdef GL_APIENTRY\n') | ||||
|       f.write('typedef void (GL_APIENTRY *RGLGENGLDEBUGPROC)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*, GLvoid*);\n') | ||||
|       f.write('typedef void (GL_APIENTRY *RGLGENGLDEBUGPROCKHR)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*, GLvoid*);\n') | ||||
|       f.write('#else\n') | ||||
|       f.write('#ifndef APIENTRY\n') | ||||
|       f.write('#define APIENTRY\n') | ||||
|       f.write('#endif\n') | ||||
|       f.write('#ifndef APIENTRYP\n') | ||||
|       f.write('#define APIENTRYP APIENTRY *\n') | ||||
|       f.write('#endif\n') | ||||
|       f.write('typedef void (APIENTRY *RGLGENGLDEBUGPROCARB)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*, GLvoid*);\n') | ||||
|       f.write('typedef void (APIENTRY *RGLGENGLDEBUGPROC)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*, GLvoid*);\n') | ||||
|       f.write('#endif\n') | ||||
| 
 | ||||
|       f.write('#ifndef GL_OES_EGL_image\n') | ||||
|       f.write('typedef void *GLeglImageOES;\n') | ||||
|       f.write('#endif\n') | ||||
| 
 | ||||
|       f.write('#if !defined(GL_OES_fixed_point) && !defined(HAVE_OPENGLES2)\n') | ||||
|       f.write('typedef GLint GLfixed;\n') | ||||
|       f.write('#endif\n') | ||||
| 
 | ||||
|       dump(f, typedefs) | ||||
|       dump(f, overrides) | ||||
|       dump(f, externs) | ||||
| 
 | ||||
|       f.write('struct rglgen_sym_map { const char *sym; void *ptr; };\n') | ||||
|       f.write('extern const struct rglgen_sym_map rglgen_symbol_map[];\n') | ||||
| 
 | ||||
|       f.write('#ifdef __cplusplus\n') | ||||
|       f.write('}\n') | ||||
|       f.write('#endif\n') | ||||
| 
 | ||||
|       f.write('#endif\n') | ||||
| 
 | ||||
|    with open(sys.argv[3], 'w') as f: | ||||
|       f.write('#include "glsym/glsym.h"\n') | ||||
|       f.write('#include <stddef.h>\n') | ||||
|       f.write('#define SYM(x) { "gl" #x, &(gl##x) }\n') | ||||
|       f.write('const struct rglgen_sym_map rglgen_symbol_map[] = {\n') | ||||
|       dump(f, macros) | ||||
|       f.write('    { NULL, NULL },\n') | ||||
|       f.write('};\n') | ||||
|       dump(f, declarations) | ||||
| 
 | ||||
|  | @ -0,0 +1,410 @@ | |||
| #include "glsym/glsym.h" | ||||
| #include <stddef.h> | ||||
| #define SYM(x) { "gl" #x, &(gl##x) } | ||||
| const struct rglgen_sym_map rglgen_symbol_map[] = { | ||||
|     SYM(BlendBarrierKHR), | ||||
|     SYM(DebugMessageControlKHR), | ||||
|     SYM(DebugMessageInsertKHR), | ||||
|     SYM(DebugMessageCallbackKHR), | ||||
|     SYM(GetDebugMessageLogKHR), | ||||
|     SYM(PushDebugGroupKHR), | ||||
|     SYM(PopDebugGroupKHR), | ||||
|     SYM(ObjectLabelKHR), | ||||
|     SYM(GetObjectLabelKHR), | ||||
|     SYM(ObjectPtrLabelKHR), | ||||
|     SYM(GetObjectPtrLabelKHR), | ||||
|     SYM(GetPointervKHR), | ||||
|     SYM(GetGraphicsResetStatusKHR), | ||||
|     SYM(ReadnPixelsKHR), | ||||
|     SYM(GetnUniformfvKHR), | ||||
|     SYM(GetnUniformivKHR), | ||||
|     SYM(GetnUniformuivKHR), | ||||
|     SYM(EGLImageTargetTexture2DOES), | ||||
|     SYM(EGLImageTargetRenderbufferStorageOES), | ||||
|     SYM(CopyImageSubDataOES), | ||||
|     SYM(EnableiOES), | ||||
|     SYM(DisableiOES), | ||||
|     SYM(BlendEquationiOES), | ||||
|     SYM(BlendEquationSeparateiOES), | ||||
|     SYM(BlendFunciOES), | ||||
|     SYM(BlendFuncSeparateiOES), | ||||
|     SYM(ColorMaskiOES), | ||||
|     SYM(IsEnablediOES), | ||||
|     SYM(DrawElementsBaseVertexOES), | ||||
|     SYM(DrawRangeElementsBaseVertexOES), | ||||
|     SYM(DrawElementsInstancedBaseVertexOES), | ||||
|     SYM(MultiDrawElementsBaseVertexOES), | ||||
|     SYM(FramebufferTextureOES), | ||||
|     SYM(GetProgramBinaryOES), | ||||
|     SYM(ProgramBinaryOES), | ||||
|     SYM(MapBufferOES), | ||||
|     SYM(UnmapBufferOES), | ||||
|     SYM(GetBufferPointervOES), | ||||
|     SYM(PrimitiveBoundingBoxOES), | ||||
|     SYM(MinSampleShadingOES), | ||||
|     SYM(PatchParameteriOES), | ||||
|     SYM(TexImage3DOES), | ||||
|     SYM(TexSubImage3DOES), | ||||
|     SYM(CopyTexSubImage3DOES), | ||||
|     SYM(CompressedTexImage3DOES), | ||||
|     SYM(CompressedTexSubImage3DOES), | ||||
|     SYM(FramebufferTexture3DOES), | ||||
|     SYM(TexParameterIivOES), | ||||
|     SYM(TexParameterIuivOES), | ||||
|     SYM(GetTexParameterIivOES), | ||||
|     SYM(GetTexParameterIuivOES), | ||||
|     SYM(SamplerParameterIivOES), | ||||
|     SYM(SamplerParameterIuivOES), | ||||
|     SYM(GetSamplerParameterIivOES), | ||||
|     SYM(GetSamplerParameterIuivOES), | ||||
|     SYM(TexBufferOES), | ||||
|     SYM(TexBufferRangeOES), | ||||
|     SYM(TexStorage3DMultisampleOES), | ||||
|     SYM(TextureViewOES), | ||||
|     SYM(BindVertexArrayOES), | ||||
|     SYM(DeleteVertexArraysOES), | ||||
|     SYM(GenVertexArraysOES), | ||||
|     SYM(IsVertexArrayOES), | ||||
|     SYM(ViewportArrayvOES), | ||||
|     SYM(ViewportIndexedfOES), | ||||
|     SYM(ViewportIndexedfvOES), | ||||
|     SYM(ScissorArrayvOES), | ||||
|     SYM(ScissorIndexedOES), | ||||
|     SYM(ScissorIndexedvOES), | ||||
|     SYM(DepthRangeArrayfvOES), | ||||
|     SYM(DepthRangeIndexedfOES), | ||||
|     SYM(GetFloati_vOES), | ||||
|     SYM(DrawArraysInstancedBaseInstanceEXT), | ||||
|     SYM(DrawElementsInstancedBaseInstanceEXT), | ||||
|     SYM(DrawElementsInstancedBaseVertexBaseInstanceEXT), | ||||
|     SYM(BindFragDataLocationIndexedEXT), | ||||
|     SYM(BindFragDataLocationEXT), | ||||
|     SYM(GetProgramResourceLocationIndexEXT), | ||||
|     SYM(GetFragDataIndexEXT), | ||||
|     SYM(BufferStorageEXT), | ||||
|     SYM(ClearTexImageEXT), | ||||
|     SYM(ClearTexSubImageEXT), | ||||
|     SYM(CopyImageSubDataEXT), | ||||
|     SYM(LabelObjectEXT), | ||||
|     SYM(GetObjectLabelEXT), | ||||
|     SYM(InsertEventMarkerEXT), | ||||
|     SYM(PushGroupMarkerEXT), | ||||
|     SYM(PopGroupMarkerEXT), | ||||
|     SYM(DiscardFramebufferEXT), | ||||
|     SYM(GenQueriesEXT), | ||||
|     SYM(DeleteQueriesEXT), | ||||
|     SYM(IsQueryEXT), | ||||
|     SYM(BeginQueryEXT), | ||||
|     SYM(EndQueryEXT), | ||||
|     SYM(QueryCounterEXT), | ||||
|     SYM(GetQueryivEXT), | ||||
|     SYM(GetQueryObjectivEXT), | ||||
|     SYM(GetQueryObjectuivEXT), | ||||
|     SYM(DrawBuffersEXT), | ||||
|     SYM(EnableiEXT), | ||||
|     SYM(DisableiEXT), | ||||
|     SYM(BlendEquationiEXT), | ||||
|     SYM(BlendEquationSeparateiEXT), | ||||
|     SYM(BlendFunciEXT), | ||||
|     SYM(BlendFuncSeparateiEXT), | ||||
|     SYM(ColorMaskiEXT), | ||||
|     SYM(IsEnablediEXT), | ||||
|     SYM(DrawElementsBaseVertexEXT), | ||||
|     SYM(DrawRangeElementsBaseVertexEXT), | ||||
|     SYM(DrawElementsInstancedBaseVertexEXT), | ||||
|     SYM(MultiDrawElementsBaseVertexEXT), | ||||
|     SYM(DrawArraysInstancedEXT), | ||||
|     SYM(DrawElementsInstancedEXT), | ||||
|     SYM(FramebufferTextureEXT), | ||||
|     SYM(VertexAttribDivisorEXT), | ||||
|     SYM(MapBufferRangeEXT), | ||||
|     SYM(FlushMappedBufferRangeEXT), | ||||
|     SYM(MultiDrawArraysEXT), | ||||
|     SYM(MultiDrawElementsEXT), | ||||
|     SYM(MultiDrawArraysIndirectEXT), | ||||
|     SYM(MultiDrawElementsIndirectEXT), | ||||
|     SYM(RenderbufferStorageMultisampleEXT), | ||||
|     SYM(FramebufferTexture2DMultisampleEXT), | ||||
|     SYM(ReadBufferIndexedEXT), | ||||
|     SYM(DrawBuffersIndexedEXT), | ||||
|     SYM(GetIntegeri_vEXT), | ||||
|     SYM(PolygonOffsetClampEXT), | ||||
|     SYM(PrimitiveBoundingBoxEXT), | ||||
|     SYM(RasterSamplesEXT), | ||||
|     SYM(GetGraphicsResetStatusEXT), | ||||
|     SYM(ReadnPixelsEXT), | ||||
|     SYM(GetnUniformfvEXT), | ||||
|     SYM(GetnUniformivEXT), | ||||
|     SYM(ActiveShaderProgramEXT), | ||||
|     SYM(BindProgramPipelineEXT), | ||||
|     SYM(CreateShaderProgramvEXT), | ||||
|     SYM(DeleteProgramPipelinesEXT), | ||||
|     SYM(GenProgramPipelinesEXT), | ||||
|     SYM(GetProgramPipelineInfoLogEXT), | ||||
|     SYM(GetProgramPipelineivEXT), | ||||
|     SYM(IsProgramPipelineEXT), | ||||
|     SYM(ProgramParameteriEXT), | ||||
|     SYM(ProgramUniform1fEXT), | ||||
|     SYM(ProgramUniform1fvEXT), | ||||
|     SYM(ProgramUniform1iEXT), | ||||
|     SYM(ProgramUniform1ivEXT), | ||||
|     SYM(ProgramUniform2fEXT), | ||||
|     SYM(ProgramUniform2fvEXT), | ||||
|     SYM(ProgramUniform2iEXT), | ||||
|     SYM(ProgramUniform2ivEXT), | ||||
|     SYM(ProgramUniform3fEXT), | ||||
|     SYM(ProgramUniform3fvEXT), | ||||
|     SYM(ProgramUniform3iEXT), | ||||
|     SYM(ProgramUniform3ivEXT), | ||||
|     SYM(ProgramUniform4fEXT), | ||||
|     SYM(ProgramUniform4fvEXT), | ||||
|     SYM(ProgramUniform4iEXT), | ||||
|     SYM(ProgramUniform4ivEXT), | ||||
|     SYM(ProgramUniformMatrix2fvEXT), | ||||
|     SYM(ProgramUniformMatrix3fvEXT), | ||||
|     SYM(ProgramUniformMatrix4fvEXT), | ||||
|     SYM(UseProgramStagesEXT), | ||||
|     SYM(ValidateProgramPipelineEXT), | ||||
|     SYM(ProgramUniform1uiEXT), | ||||
|     SYM(ProgramUniform2uiEXT), | ||||
|     SYM(ProgramUniform3uiEXT), | ||||
|     SYM(ProgramUniform4uiEXT), | ||||
|     SYM(ProgramUniform1uivEXT), | ||||
|     SYM(ProgramUniform2uivEXT), | ||||
|     SYM(ProgramUniform3uivEXT), | ||||
|     SYM(ProgramUniform4uivEXT), | ||||
|     SYM(ProgramUniformMatrix2x3fvEXT), | ||||
|     SYM(ProgramUniformMatrix3x2fvEXT), | ||||
|     SYM(ProgramUniformMatrix2x4fvEXT), | ||||
|     SYM(ProgramUniformMatrix4x2fvEXT), | ||||
|     SYM(ProgramUniformMatrix3x4fvEXT), | ||||
|     SYM(ProgramUniformMatrix4x3fvEXT), | ||||
|     SYM(FramebufferPixelLocalStorageSizeEXT), | ||||
|     SYM(GetFramebufferPixelLocalStorageSizeEXT), | ||||
|     SYM(ClearPixelLocalStorageuiEXT), | ||||
|     SYM(TexPageCommitmentEXT), | ||||
|     SYM(PatchParameteriEXT), | ||||
|     SYM(TexParameterIivEXT), | ||||
|     SYM(TexParameterIuivEXT), | ||||
|     SYM(GetTexParameterIivEXT), | ||||
|     SYM(GetTexParameterIuivEXT), | ||||
|     SYM(SamplerParameterIivEXT), | ||||
|     SYM(SamplerParameterIuivEXT), | ||||
|     SYM(GetSamplerParameterIivEXT), | ||||
|     SYM(GetSamplerParameterIuivEXT), | ||||
|     SYM(TexBufferEXT), | ||||
|     SYM(TexBufferRangeEXT), | ||||
|     SYM(TexStorage1DEXT), | ||||
|     SYM(TexStorage2DEXT), | ||||
|     SYM(TexStorage3DEXT), | ||||
|     SYM(TextureStorage1DEXT), | ||||
|     SYM(TextureStorage2DEXT), | ||||
|     SYM(TextureStorage3DEXT), | ||||
|     SYM(TextureViewEXT), | ||||
|     SYM(FramebufferTextureMultiviewOVR), | ||||
|     SYM(FramebufferTextureMultisampleMultiviewOVR), | ||||
| 
 | ||||
|     { NULL, NULL }, | ||||
| }; | ||||
| RGLSYMGLBLENDBARRIERKHRPROC __rglgen_glBlendBarrierKHR; | ||||
| RGLSYMGLDEBUGMESSAGECONTROLKHRPROC __rglgen_glDebugMessageControlKHR; | ||||
| RGLSYMGLDEBUGMESSAGEINSERTKHRPROC __rglgen_glDebugMessageInsertKHR; | ||||
| RGLSYMGLDEBUGMESSAGECALLBACKKHRPROC __rglgen_glDebugMessageCallbackKHR; | ||||
| RGLSYMGLGETDEBUGMESSAGELOGKHRPROC __rglgen_glGetDebugMessageLogKHR; | ||||
| RGLSYMGLPUSHDEBUGGROUPKHRPROC __rglgen_glPushDebugGroupKHR; | ||||
| RGLSYMGLPOPDEBUGGROUPKHRPROC __rglgen_glPopDebugGroupKHR; | ||||
| RGLSYMGLOBJECTLABELKHRPROC __rglgen_glObjectLabelKHR; | ||||
| RGLSYMGLGETOBJECTLABELKHRPROC __rglgen_glGetObjectLabelKHR; | ||||
| RGLSYMGLOBJECTPTRLABELKHRPROC __rglgen_glObjectPtrLabelKHR; | ||||
| RGLSYMGLGETOBJECTPTRLABELKHRPROC __rglgen_glGetObjectPtrLabelKHR; | ||||
| RGLSYMGLGETPOINTERVKHRPROC __rglgen_glGetPointervKHR; | ||||
| RGLSYMGLGETGRAPHICSRESETSTATUSKHRPROC __rglgen_glGetGraphicsResetStatusKHR; | ||||
| RGLSYMGLREADNPIXELSKHRPROC __rglgen_glReadnPixelsKHR; | ||||
| RGLSYMGLGETNUNIFORMFVKHRPROC __rglgen_glGetnUniformfvKHR; | ||||
| RGLSYMGLGETNUNIFORMIVKHRPROC __rglgen_glGetnUniformivKHR; | ||||
| RGLSYMGLGETNUNIFORMUIVKHRPROC __rglgen_glGetnUniformuivKHR; | ||||
| RGLSYMGLEGLIMAGETARGETTEXTURE2DOESPROC __rglgen_glEGLImageTargetTexture2DOES; | ||||
| RGLSYMGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC __rglgen_glEGLImageTargetRenderbufferStorageOES; | ||||
| RGLSYMGLCOPYIMAGESUBDATAOESPROC __rglgen_glCopyImageSubDataOES; | ||||
| RGLSYMGLENABLEIOESPROC __rglgen_glEnableiOES; | ||||
| RGLSYMGLDISABLEIOESPROC __rglgen_glDisableiOES; | ||||
| RGLSYMGLBLENDEQUATIONIOESPROC __rglgen_glBlendEquationiOES; | ||||
| RGLSYMGLBLENDEQUATIONSEPARATEIOESPROC __rglgen_glBlendEquationSeparateiOES; | ||||
| RGLSYMGLBLENDFUNCIOESPROC __rglgen_glBlendFunciOES; | ||||
| RGLSYMGLBLENDFUNCSEPARATEIOESPROC __rglgen_glBlendFuncSeparateiOES; | ||||
| RGLSYMGLCOLORMASKIOESPROC __rglgen_glColorMaskiOES; | ||||
| RGLSYMGLISENABLEDIOESPROC __rglgen_glIsEnablediOES; | ||||
| RGLSYMGLDRAWELEMENTSBASEVERTEXOESPROC __rglgen_glDrawElementsBaseVertexOES; | ||||
| RGLSYMGLDRAWRANGEELEMENTSBASEVERTEXOESPROC __rglgen_glDrawRangeElementsBaseVertexOES; | ||||
| RGLSYMGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC __rglgen_glDrawElementsInstancedBaseVertexOES; | ||||
| RGLSYMGLMULTIDRAWELEMENTSBASEVERTEXOESPROC __rglgen_glMultiDrawElementsBaseVertexOES; | ||||
| RGLSYMGLFRAMEBUFFERTEXTUREOESPROC __rglgen_glFramebufferTextureOES; | ||||
| RGLSYMGLGETPROGRAMBINARYOESPROC __rglgen_glGetProgramBinaryOES; | ||||
| RGLSYMGLPROGRAMBINARYOESPROC __rglgen_glProgramBinaryOES; | ||||
| RGLSYMGLMAPBUFFEROESPROC __rglgen_glMapBufferOES; | ||||
| RGLSYMGLUNMAPBUFFEROESPROC __rglgen_glUnmapBufferOES; | ||||
| RGLSYMGLGETBUFFERPOINTERVOESPROC __rglgen_glGetBufferPointervOES; | ||||
| RGLSYMGLPRIMITIVEBOUNDINGBOXOESPROC __rglgen_glPrimitiveBoundingBoxOES; | ||||
| RGLSYMGLMINSAMPLESHADINGOESPROC __rglgen_glMinSampleShadingOES; | ||||
| RGLSYMGLPATCHPARAMETERIOESPROC __rglgen_glPatchParameteriOES; | ||||
| RGLSYMGLTEXIMAGE3DOESPROC __rglgen_glTexImage3DOES; | ||||
| RGLSYMGLTEXSUBIMAGE3DOESPROC __rglgen_glTexSubImage3DOES; | ||||
| RGLSYMGLCOPYTEXSUBIMAGE3DOESPROC __rglgen_glCopyTexSubImage3DOES; | ||||
| RGLSYMGLCOMPRESSEDTEXIMAGE3DOESPROC __rglgen_glCompressedTexImage3DOES; | ||||
| RGLSYMGLCOMPRESSEDTEXSUBIMAGE3DOESPROC __rglgen_glCompressedTexSubImage3DOES; | ||||
| RGLSYMGLFRAMEBUFFERTEXTURE3DOESPROC __rglgen_glFramebufferTexture3DOES; | ||||
| RGLSYMGLTEXPARAMETERIIVOESPROC __rglgen_glTexParameterIivOES; | ||||
| RGLSYMGLTEXPARAMETERIUIVOESPROC __rglgen_glTexParameterIuivOES; | ||||
| RGLSYMGLGETTEXPARAMETERIIVOESPROC __rglgen_glGetTexParameterIivOES; | ||||
| RGLSYMGLGETTEXPARAMETERIUIVOESPROC __rglgen_glGetTexParameterIuivOES; | ||||
| RGLSYMGLSAMPLERPARAMETERIIVOESPROC __rglgen_glSamplerParameterIivOES; | ||||
| RGLSYMGLSAMPLERPARAMETERIUIVOESPROC __rglgen_glSamplerParameterIuivOES; | ||||
| RGLSYMGLGETSAMPLERPARAMETERIIVOESPROC __rglgen_glGetSamplerParameterIivOES; | ||||
| RGLSYMGLGETSAMPLERPARAMETERIUIVOESPROC __rglgen_glGetSamplerParameterIuivOES; | ||||
| RGLSYMGLTEXBUFFEROESPROC __rglgen_glTexBufferOES; | ||||
| RGLSYMGLTEXBUFFERRANGEOESPROC __rglgen_glTexBufferRangeOES; | ||||
| RGLSYMGLTEXSTORAGE3DMULTISAMPLEOESPROC __rglgen_glTexStorage3DMultisampleOES; | ||||
| RGLSYMGLTEXTUREVIEWOESPROC __rglgen_glTextureViewOES; | ||||
| RGLSYMGLBINDVERTEXARRAYOESPROC __rglgen_glBindVertexArrayOES; | ||||
| RGLSYMGLDELETEVERTEXARRAYSOESPROC __rglgen_glDeleteVertexArraysOES; | ||||
| RGLSYMGLGENVERTEXARRAYSOESPROC __rglgen_glGenVertexArraysOES; | ||||
| RGLSYMGLISVERTEXARRAYOESPROC __rglgen_glIsVertexArrayOES; | ||||
| RGLSYMGLVIEWPORTARRAYVOESPROC __rglgen_glViewportArrayvOES; | ||||
| RGLSYMGLVIEWPORTINDEXEDFOESPROC __rglgen_glViewportIndexedfOES; | ||||
| RGLSYMGLVIEWPORTINDEXEDFVOESPROC __rglgen_glViewportIndexedfvOES; | ||||
| RGLSYMGLSCISSORARRAYVOESPROC __rglgen_glScissorArrayvOES; | ||||
| RGLSYMGLSCISSORINDEXEDOESPROC __rglgen_glScissorIndexedOES; | ||||
| RGLSYMGLSCISSORINDEXEDVOESPROC __rglgen_glScissorIndexedvOES; | ||||
| RGLSYMGLDEPTHRANGEARRAYFVOESPROC __rglgen_glDepthRangeArrayfvOES; | ||||
| RGLSYMGLDEPTHRANGEINDEXEDFOESPROC __rglgen_glDepthRangeIndexedfOES; | ||||
| RGLSYMGLGETFLOATI_VOESPROC __rglgen_glGetFloati_vOES; | ||||
| RGLSYMGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC __rglgen_glDrawArraysInstancedBaseInstanceEXT; | ||||
| RGLSYMGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC __rglgen_glDrawElementsInstancedBaseInstanceEXT; | ||||
| RGLSYMGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC __rglgen_glDrawElementsInstancedBaseVertexBaseInstanceEXT; | ||||
| RGLSYMGLBINDFRAGDATALOCATIONINDEXEDEXTPROC __rglgen_glBindFragDataLocationIndexedEXT; | ||||
| RGLSYMGLBINDFRAGDATALOCATIONEXTPROC __rglgen_glBindFragDataLocationEXT; | ||||
| RGLSYMGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC __rglgen_glGetProgramResourceLocationIndexEXT; | ||||
| RGLSYMGLGETFRAGDATAINDEXEXTPROC __rglgen_glGetFragDataIndexEXT; | ||||
| RGLSYMGLBUFFERSTORAGEEXTPROC __rglgen_glBufferStorageEXT; | ||||
| RGLSYMGLCLEARTEXIMAGEEXTPROC __rglgen_glClearTexImageEXT; | ||||
| RGLSYMGLCLEARTEXSUBIMAGEEXTPROC __rglgen_glClearTexSubImageEXT; | ||||
| RGLSYMGLCOPYIMAGESUBDATAEXTPROC __rglgen_glCopyImageSubDataEXT; | ||||
| RGLSYMGLLABELOBJECTEXTPROC __rglgen_glLabelObjectEXT; | ||||
| RGLSYMGLGETOBJECTLABELEXTPROC __rglgen_glGetObjectLabelEXT; | ||||
| RGLSYMGLINSERTEVENTMARKEREXTPROC __rglgen_glInsertEventMarkerEXT; | ||||
| RGLSYMGLPUSHGROUPMARKEREXTPROC __rglgen_glPushGroupMarkerEXT; | ||||
| RGLSYMGLPOPGROUPMARKEREXTPROC __rglgen_glPopGroupMarkerEXT; | ||||
| RGLSYMGLDISCARDFRAMEBUFFEREXTPROC __rglgen_glDiscardFramebufferEXT; | ||||
| RGLSYMGLGENQUERIESEXTPROC __rglgen_glGenQueriesEXT; | ||||
| RGLSYMGLDELETEQUERIESEXTPROC __rglgen_glDeleteQueriesEXT; | ||||
| RGLSYMGLISQUERYEXTPROC __rglgen_glIsQueryEXT; | ||||
| RGLSYMGLBEGINQUERYEXTPROC __rglgen_glBeginQueryEXT; | ||||
| RGLSYMGLENDQUERYEXTPROC __rglgen_glEndQueryEXT; | ||||
| RGLSYMGLQUERYCOUNTEREXTPROC __rglgen_glQueryCounterEXT; | ||||
| RGLSYMGLGETQUERYIVEXTPROC __rglgen_glGetQueryivEXT; | ||||
| RGLSYMGLGETQUERYOBJECTIVEXTPROC __rglgen_glGetQueryObjectivEXT; | ||||
| RGLSYMGLGETQUERYOBJECTUIVEXTPROC __rglgen_glGetQueryObjectuivEXT; | ||||
| RGLSYMGLDRAWBUFFERSEXTPROC __rglgen_glDrawBuffersEXT; | ||||
| RGLSYMGLENABLEIEXTPROC __rglgen_glEnableiEXT; | ||||
| RGLSYMGLDISABLEIEXTPROC __rglgen_glDisableiEXT; | ||||
| RGLSYMGLBLENDEQUATIONIEXTPROC __rglgen_glBlendEquationiEXT; | ||||
| RGLSYMGLBLENDEQUATIONSEPARATEIEXTPROC __rglgen_glBlendEquationSeparateiEXT; | ||||
| RGLSYMGLBLENDFUNCIEXTPROC __rglgen_glBlendFunciEXT; | ||||
| RGLSYMGLBLENDFUNCSEPARATEIEXTPROC __rglgen_glBlendFuncSeparateiEXT; | ||||
| RGLSYMGLCOLORMASKIEXTPROC __rglgen_glColorMaskiEXT; | ||||
| RGLSYMGLISENABLEDIEXTPROC __rglgen_glIsEnablediEXT; | ||||
| RGLSYMGLDRAWELEMENTSBASEVERTEXEXTPROC __rglgen_glDrawElementsBaseVertexEXT; | ||||
| RGLSYMGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC __rglgen_glDrawRangeElementsBaseVertexEXT; | ||||
| RGLSYMGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC __rglgen_glDrawElementsInstancedBaseVertexEXT; | ||||
| RGLSYMGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC __rglgen_glMultiDrawElementsBaseVertexEXT; | ||||
| RGLSYMGLDRAWARRAYSINSTANCEDEXTPROC __rglgen_glDrawArraysInstancedEXT; | ||||
| RGLSYMGLDRAWELEMENTSINSTANCEDEXTPROC __rglgen_glDrawElementsInstancedEXT; | ||||
| RGLSYMGLFRAMEBUFFERTEXTUREEXTPROC __rglgen_glFramebufferTextureEXT; | ||||
| RGLSYMGLVERTEXATTRIBDIVISOREXTPROC __rglgen_glVertexAttribDivisorEXT; | ||||
| RGLSYMGLMAPBUFFERRANGEEXTPROC __rglgen_glMapBufferRangeEXT; | ||||
| RGLSYMGLFLUSHMAPPEDBUFFERRANGEEXTPROC __rglgen_glFlushMappedBufferRangeEXT; | ||||
| RGLSYMGLMULTIDRAWARRAYSEXTPROC __rglgen_glMultiDrawArraysEXT; | ||||
| RGLSYMGLMULTIDRAWELEMENTSEXTPROC __rglgen_glMultiDrawElementsEXT; | ||||
| RGLSYMGLMULTIDRAWARRAYSINDIRECTEXTPROC __rglgen_glMultiDrawArraysIndirectEXT; | ||||
| RGLSYMGLMULTIDRAWELEMENTSINDIRECTEXTPROC __rglgen_glMultiDrawElementsIndirectEXT; | ||||
| RGLSYMGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC __rglgen_glRenderbufferStorageMultisampleEXT; | ||||
| RGLSYMGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC __rglgen_glFramebufferTexture2DMultisampleEXT; | ||||
| RGLSYMGLREADBUFFERINDEXEDEXTPROC __rglgen_glReadBufferIndexedEXT; | ||||
| RGLSYMGLDRAWBUFFERSINDEXEDEXTPROC __rglgen_glDrawBuffersIndexedEXT; | ||||
| RGLSYMGLGETINTEGERI_VEXTPROC __rglgen_glGetIntegeri_vEXT; | ||||
| RGLSYMGLPOLYGONOFFSETCLAMPEXTPROC __rglgen_glPolygonOffsetClampEXT; | ||||
| RGLSYMGLPRIMITIVEBOUNDINGBOXEXTPROC __rglgen_glPrimitiveBoundingBoxEXT; | ||||
| RGLSYMGLRASTERSAMPLESEXTPROC __rglgen_glRasterSamplesEXT; | ||||
| RGLSYMGLGETGRAPHICSRESETSTATUSEXTPROC __rglgen_glGetGraphicsResetStatusEXT; | ||||
| RGLSYMGLREADNPIXELSEXTPROC __rglgen_glReadnPixelsEXT; | ||||
| RGLSYMGLGETNUNIFORMFVEXTPROC __rglgen_glGetnUniformfvEXT; | ||||
| RGLSYMGLGETNUNIFORMIVEXTPROC __rglgen_glGetnUniformivEXT; | ||||
| RGLSYMGLACTIVESHADERPROGRAMEXTPROC __rglgen_glActiveShaderProgramEXT; | ||||
| RGLSYMGLBINDPROGRAMPIPELINEEXTPROC __rglgen_glBindProgramPipelineEXT; | ||||
| RGLSYMGLCREATESHADERPROGRAMVEXTPROC __rglgen_glCreateShaderProgramvEXT; | ||||
| RGLSYMGLDELETEPROGRAMPIPELINESEXTPROC __rglgen_glDeleteProgramPipelinesEXT; | ||||
| RGLSYMGLGENPROGRAMPIPELINESEXTPROC __rglgen_glGenProgramPipelinesEXT; | ||||
| RGLSYMGLGETPROGRAMPIPELINEINFOLOGEXTPROC __rglgen_glGetProgramPipelineInfoLogEXT; | ||||
| RGLSYMGLGETPROGRAMPIPELINEIVEXTPROC __rglgen_glGetProgramPipelineivEXT; | ||||
| RGLSYMGLISPROGRAMPIPELINEEXTPROC __rglgen_glIsProgramPipelineEXT; | ||||
| RGLSYMGLPROGRAMPARAMETERIEXTPROC __rglgen_glProgramParameteriEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM1FEXTPROC __rglgen_glProgramUniform1fEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM1FVEXTPROC __rglgen_glProgramUniform1fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM1IEXTPROC __rglgen_glProgramUniform1iEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM1IVEXTPROC __rglgen_glProgramUniform1ivEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM2FEXTPROC __rglgen_glProgramUniform2fEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM2FVEXTPROC __rglgen_glProgramUniform2fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM2IEXTPROC __rglgen_glProgramUniform2iEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM2IVEXTPROC __rglgen_glProgramUniform2ivEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM3FEXTPROC __rglgen_glProgramUniform3fEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM3FVEXTPROC __rglgen_glProgramUniform3fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM3IEXTPROC __rglgen_glProgramUniform3iEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM3IVEXTPROC __rglgen_glProgramUniform3ivEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM4FEXTPROC __rglgen_glProgramUniform4fEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM4FVEXTPROC __rglgen_glProgramUniform4fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM4IEXTPROC __rglgen_glProgramUniform4iEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM4IVEXTPROC __rglgen_glProgramUniform4ivEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX2FVEXTPROC __rglgen_glProgramUniformMatrix2fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX3FVEXTPROC __rglgen_glProgramUniformMatrix3fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX4FVEXTPROC __rglgen_glProgramUniformMatrix4fvEXT; | ||||
| RGLSYMGLUSEPROGRAMSTAGESEXTPROC __rglgen_glUseProgramStagesEXT; | ||||
| RGLSYMGLVALIDATEPROGRAMPIPELINEEXTPROC __rglgen_glValidateProgramPipelineEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM1UIEXTPROC __rglgen_glProgramUniform1uiEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM2UIEXTPROC __rglgen_glProgramUniform2uiEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM3UIEXTPROC __rglgen_glProgramUniform3uiEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM4UIEXTPROC __rglgen_glProgramUniform4uiEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM1UIVEXTPROC __rglgen_glProgramUniform1uivEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM2UIVEXTPROC __rglgen_glProgramUniform2uivEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM3UIVEXTPROC __rglgen_glProgramUniform3uivEXT; | ||||
| RGLSYMGLPROGRAMUNIFORM4UIVEXTPROC __rglgen_glProgramUniform4uivEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC __rglgen_glProgramUniformMatrix2x3fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC __rglgen_glProgramUniformMatrix3x2fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC __rglgen_glProgramUniformMatrix2x4fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC __rglgen_glProgramUniformMatrix4x2fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC __rglgen_glProgramUniformMatrix3x4fvEXT; | ||||
| RGLSYMGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC __rglgen_glProgramUniformMatrix4x3fvEXT; | ||||
| RGLSYMGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC __rglgen_glFramebufferPixelLocalStorageSizeEXT; | ||||
| RGLSYMGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC __rglgen_glGetFramebufferPixelLocalStorageSizeEXT; | ||||
| RGLSYMGLCLEARPIXELLOCALSTORAGEUIEXTPROC __rglgen_glClearPixelLocalStorageuiEXT; | ||||
| RGLSYMGLTEXPAGECOMMITMENTEXTPROC __rglgen_glTexPageCommitmentEXT; | ||||
| RGLSYMGLPATCHPARAMETERIEXTPROC __rglgen_glPatchParameteriEXT; | ||||
| RGLSYMGLTEXPARAMETERIIVEXTPROC __rglgen_glTexParameterIivEXT; | ||||
| RGLSYMGLTEXPARAMETERIUIVEXTPROC __rglgen_glTexParameterIuivEXT; | ||||
| RGLSYMGLGETTEXPARAMETERIIVEXTPROC __rglgen_glGetTexParameterIivEXT; | ||||
| RGLSYMGLGETTEXPARAMETERIUIVEXTPROC __rglgen_glGetTexParameterIuivEXT; | ||||
| RGLSYMGLSAMPLERPARAMETERIIVEXTPROC __rglgen_glSamplerParameterIivEXT; | ||||
| RGLSYMGLSAMPLERPARAMETERIUIVEXTPROC __rglgen_glSamplerParameterIuivEXT; | ||||
| RGLSYMGLGETSAMPLERPARAMETERIIVEXTPROC __rglgen_glGetSamplerParameterIivEXT; | ||||
| RGLSYMGLGETSAMPLERPARAMETERIUIVEXTPROC __rglgen_glGetSamplerParameterIuivEXT; | ||||
| RGLSYMGLTEXBUFFEREXTPROC __rglgen_glTexBufferEXT; | ||||
| RGLSYMGLTEXBUFFERRANGEEXTPROC __rglgen_glTexBufferRangeEXT; | ||||
| RGLSYMGLTEXSTORAGE1DEXTPROC __rglgen_glTexStorage1DEXT; | ||||
| RGLSYMGLTEXSTORAGE2DEXTPROC __rglgen_glTexStorage2DEXT; | ||||
| RGLSYMGLTEXSTORAGE3DEXTPROC __rglgen_glTexStorage3DEXT; | ||||
| RGLSYMGLTEXTURESTORAGE1DEXTPROC __rglgen_glTextureStorage1DEXT; | ||||
| RGLSYMGLTEXTURESTORAGE2DEXTPROC __rglgen_glTextureStorage2DEXT; | ||||
| RGLSYMGLTEXTURESTORAGE3DEXTPROC __rglgen_glTextureStorage3DEXT; | ||||
| RGLSYMGLTEXTUREVIEWEXTPROC __rglgen_glTextureViewEXT; | ||||
| RGLSYMGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC __rglgen_glFramebufferTextureMultiviewOVR; | ||||
| RGLSYMGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC __rglgen_glFramebufferTextureMultisampleMultiviewOVR; | ||||
| 
 | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue