mirror of
https://github.com/libretro/citra.git
synced 2025-03-01 04:35:27 +00:00
update libretro-common
This commit is contained in:
parent
9106ca4fe3
commit
5e56e468ce
7
externals/libretro-common/.gitignore
vendored
7
externals/libretro-common/.gitignore
vendored
@ -1 +1,6 @@
|
||||
*.o
|
||||
glsm/
|
||||
*.[od]
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.exe
|
||||
|
145
externals/libretro-common/audio/audio_mix.c
vendored
145
externals/libretro-common/audio/audio_mix.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_mix.c).
|
||||
@ -20,7 +20,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <audio/audio_mix.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <memalign.h>
|
||||
|
||||
#include <retro_environment.h>
|
||||
|
||||
#if defined(__SSE2__)
|
||||
#include <emmintrin.h>
|
||||
@ -28,10 +33,6 @@
|
||||
#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>
|
||||
@ -48,7 +49,7 @@ void audio_mix_volume_C(float *out, const float *in, float vol, size_t samples)
|
||||
#ifdef __SSE2__
|
||||
void audio_mix_volume_SSE2(float *out, const float *in, float vol, size_t samples)
|
||||
{
|
||||
size_t i;
|
||||
size_t i, remaining_samples;
|
||||
__m128 volume = _mm_set1_ps(vol);
|
||||
|
||||
for (i = 0; i + 16 <= samples; i += 16, out += 16, in += 16)
|
||||
@ -71,7 +72,10 @@ void audio_mix_volume_SSE2(float *out, const float *in, float vol, size_t sample
|
||||
_mm_storeu_ps(out + 4 * j, _mm_add_ps(input[j], additive[j]));
|
||||
}
|
||||
|
||||
audio_mix_volume_C(out, in, vol, samples - i);
|
||||
remaining_samples = samples - i;
|
||||
|
||||
for (i = 0; i < remaining_samples; i++)
|
||||
out[i] += in[i] * vol;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -80,12 +84,14 @@ void audio_mix_free_chunk(audio_chunk_t *chunk)
|
||||
if (!chunk)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav && chunk->rwav->samples)
|
||||
{
|
||||
/* rwav_free only frees the samples */
|
||||
rwav_free(chunk->rwav);
|
||||
free(chunk->rwav);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (chunk->buf)
|
||||
free(chunk->buf);
|
||||
@ -110,24 +116,44 @@ void audio_mix_free_chunk(audio_chunk_t *chunk)
|
||||
|
||||
audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate)
|
||||
{
|
||||
#ifdef HAVE_RWAV
|
||||
int sample_size;
|
||||
int64_t len = 0;
|
||||
void *buf = NULL;
|
||||
audio_chunk_t *chunk = (audio_chunk_t*)calloc(1, sizeof(*chunk));
|
||||
int64_t len = 0;
|
||||
void *buf = NULL;
|
||||
audio_chunk_t *chunk = (audio_chunk_t*)malloc(sizeof(*chunk));
|
||||
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
|
||||
chunk->buf = NULL;
|
||||
chunk->upsample_buf = NULL;
|
||||
chunk->float_buf = NULL;
|
||||
chunk->float_resample_buf = NULL;
|
||||
chunk->resample_buf = NULL;
|
||||
chunk->len = 0;
|
||||
chunk->resample_len = 0;
|
||||
chunk->rwav = (rwav_t*)malloc(sizeof(rwav_t));
|
||||
chunk->sample_rate = sample_rate;
|
||||
chunk->resample = false;
|
||||
chunk->resampler = NULL;
|
||||
chunk->resampler_data = NULL;
|
||||
chunk->ratio = 0.00f;
|
||||
|
||||
chunk->rwav->bitspersample = 0;
|
||||
chunk->rwav->numchannels = 0;
|
||||
chunk->rwav->samplerate = 0;
|
||||
chunk->rwav->numsamples = 0;
|
||||
chunk->rwav->subchunk2size = 0;
|
||||
chunk->rwav->samples = NULL;
|
||||
|
||||
if (!filestream_read_file(path, &buf, &len))
|
||||
{
|
||||
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));
|
||||
chunk->buf = buf;
|
||||
chunk->len = len;
|
||||
|
||||
if (rwav_load(chunk->rwav, chunk->buf, chunk->len) == RWAV_ITERATE_ERROR)
|
||||
{
|
||||
@ -137,28 +163,42 @@ audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate)
|
||||
|
||||
/* numsamples does not know or care about
|
||||
* multiple channels, but we need space for 2 */
|
||||
chunk->upsample_buf = (int16_t*)memalign_alloc(128,
|
||||
chunk->upsample_buf = (int16_t*)memalign_alloc(128,
|
||||
chunk->rwav->numsamples * 2 * sizeof(int16_t));
|
||||
|
||||
sample_size = chunk->rwav->bitspersample / 8;
|
||||
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);
|
||||
if (chunk->rwav->numchannels == 1)
|
||||
{
|
||||
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||
{
|
||||
uint8_t *sample = (
|
||||
(uint8_t*)chunk->rwav->samples) + i;
|
||||
|
||||
chunk->upsample_buf[i * 2] = (int16_t)((sample[0] - 128) << 8);
|
||||
chunk->upsample_buf[i * 2] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
chunk->upsample_buf[(i * 2) + 1] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
}
|
||||
}
|
||||
else if (chunk->rwav->numchannels == 2)
|
||||
{
|
||||
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||
{
|
||||
uint8_t *sample = (
|
||||
(uint8_t*)chunk->rwav->samples) +
|
||||
(i * 2);
|
||||
|
||||
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);
|
||||
}
|
||||
chunk->upsample_buf[i * 2] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
chunk->upsample_buf[(i * 2) + 1] =
|
||||
(int16_t)((sample[1] - 128) << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sample_size == 2)
|
||||
{
|
||||
@ -168,14 +208,16 @@ audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate)
|
||||
|
||||
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||
{
|
||||
int16_t sample = ((int16_t*)chunk->rwav->samples)[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);
|
||||
memcpy(chunk->upsample_buf, chunk->rwav->samples,
|
||||
chunk->rwav->subchunk2size);
|
||||
}
|
||||
else if (sample_size != 2)
|
||||
{
|
||||
@ -187,7 +229,7 @@ audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate)
|
||||
if (sample_rate != (int)chunk->rwav->samplerate)
|
||||
{
|
||||
chunk->resample = true;
|
||||
chunk->ratio = (double)sample_rate / chunk->rwav->samplerate;
|
||||
chunk->ratio = (double)sample_rate / chunk->rwav->samplerate;
|
||||
|
||||
retro_resampler_realloc(&chunk->resampler_data,
|
||||
&chunk->resampler,
|
||||
@ -199,12 +241,18 @@ audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate)
|
||||
{
|
||||
struct resampler_data info;
|
||||
|
||||
chunk->float_buf = (float*)memalign_alloc(128, chunk->rwav->numsamples * 2 * chunk->ratio * sizeof(float));
|
||||
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));
|
||||
/* 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);
|
||||
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;
|
||||
@ -216,10 +264,13 @@ audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate)
|
||||
|
||||
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));
|
||||
/* 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);
|
||||
convert_float_to_s16(chunk->resample_buf,
|
||||
chunk->float_resample_buf, info.output_frames * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,6 +278,7 @@ audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate)
|
||||
|
||||
error:
|
||||
audio_mix_free_chunk(chunk);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -235,12 +287,14 @@ size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk)
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
{
|
||||
if (chunk->resample)
|
||||
return chunk->resample_len;
|
||||
return chunk->rwav->numsamples;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* no other filetypes supported yet */
|
||||
return 0;
|
||||
@ -256,11 +310,13 @@ size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk)
|
||||
*
|
||||
* Returns: A signed 16-bit audio sample.
|
||||
**/
|
||||
int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk, unsigned channel, size_t index)
|
||||
int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk,
|
||||
unsigned channel, size_t index)
|
||||
{
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
{
|
||||
int sample_size = chunk->rwav->bitspersample / 8;
|
||||
@ -271,15 +327,18 @@ int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk, unsigned channel, size_
|
||||
|
||||
if (chunk->resample)
|
||||
sample = (uint8_t*)chunk->resample_buf +
|
||||
(sample_size * index * chunk->rwav->numchannels) + (channel * sample_size);
|
||||
(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_size * index * chunk->rwav->numchannels)
|
||||
+ (channel * sample_size);
|
||||
|
||||
sample_out = (int16_t)*sample;
|
||||
|
||||
return sample_out;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* no other filetypes supported yet */
|
||||
return 0;
|
||||
@ -290,6 +349,7 @@ int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk)
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
{
|
||||
int16_t *sample;
|
||||
@ -301,6 +361,7 @@ int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk)
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -310,8 +371,10 @@ int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk)
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
return chunk->rwav->numchannels;
|
||||
#endif
|
||||
|
||||
/* don't support other formats yet */
|
||||
return 0;
|
||||
|
251
externals/libretro-common/audio/audio_mixer.c
vendored
251
externals/libretro-common/audio/audio_mixer.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_mixer.c).
|
||||
@ -20,10 +20,16 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#include <audio/audio_mixer.h>
|
||||
#include <audio/audio_resampler.h>
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
#include <formats/rwav.h>
|
||||
#endif
|
||||
#include <memalign.h>
|
||||
|
||||
#include <stdio.h>
|
||||
@ -31,10 +37,6 @@
|
||||
#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
|
||||
@ -71,16 +73,16 @@ struct audio_mixer_sound
|
||||
struct
|
||||
{
|
||||
/* wav */
|
||||
unsigned frames;
|
||||
const float* pcm;
|
||||
unsigned frames;
|
||||
} wav;
|
||||
|
||||
#ifdef HAVE_STB_VORBIS
|
||||
struct
|
||||
{
|
||||
/* ogg */
|
||||
unsigned size;
|
||||
const void* data;
|
||||
unsigned size;
|
||||
} ogg;
|
||||
#endif
|
||||
|
||||
@ -88,8 +90,8 @@ struct audio_mixer_sound
|
||||
struct
|
||||
{
|
||||
/* flac */
|
||||
unsigned size;
|
||||
const void* data;
|
||||
unsigned size;
|
||||
} flac;
|
||||
#endif
|
||||
|
||||
@ -97,8 +99,8 @@ struct audio_mixer_sound
|
||||
struct
|
||||
{
|
||||
/* mp */
|
||||
unsigned size;
|
||||
const void* data;
|
||||
unsigned size;
|
||||
} mp3;
|
||||
#endif
|
||||
|
||||
@ -106,8 +108,8 @@ struct audio_mixer_sound
|
||||
struct
|
||||
{
|
||||
/* mod/s3m/xm */
|
||||
unsigned size;
|
||||
const void* data;
|
||||
unsigned size;
|
||||
} mod;
|
||||
#endif
|
||||
} types;
|
||||
@ -115,12 +117,6 @@ struct audio_mixer_sound
|
||||
|
||||
struct audio_mixer_voice
|
||||
{
|
||||
bool repeat;
|
||||
unsigned type;
|
||||
float volume;
|
||||
audio_mixer_sound_t *sound;
|
||||
audio_mixer_stop_cb_t stop_cb;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
@ -131,63 +127,71 @@ struct audio_mixer_voice
|
||||
#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;
|
||||
float *buffer;
|
||||
unsigned position;
|
||||
unsigned samples;
|
||||
unsigned buf_samples;
|
||||
float ratio;
|
||||
} ogg;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DR_FLAC
|
||||
struct
|
||||
{
|
||||
unsigned position;
|
||||
unsigned samples;
|
||||
unsigned buf_samples;
|
||||
float* buffer;
|
||||
float ratio;
|
||||
drflac *stream;
|
||||
void *resampler_data;
|
||||
const retro_resampler_t *resampler;
|
||||
unsigned position;
|
||||
unsigned samples;
|
||||
unsigned buf_samples;
|
||||
float ratio;
|
||||
} flac;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DR_MP3
|
||||
struct
|
||||
{
|
||||
unsigned position;
|
||||
unsigned samples;
|
||||
unsigned buf_samples;
|
||||
float* buffer;
|
||||
float ratio;
|
||||
drmp3 stream;
|
||||
void *resampler_data;
|
||||
const retro_resampler_t *resampler;
|
||||
float* buffer;
|
||||
unsigned position;
|
||||
unsigned samples;
|
||||
unsigned buf_samples;
|
||||
float ratio;
|
||||
} mp3;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IBXM
|
||||
struct
|
||||
{
|
||||
unsigned position;
|
||||
unsigned samples;
|
||||
unsigned buf_samples;
|
||||
int* buffer;
|
||||
struct replay* stream;
|
||||
struct module* module;
|
||||
unsigned position;
|
||||
unsigned samples;
|
||||
unsigned buf_samples;
|
||||
} mod;
|
||||
#endif
|
||||
} types;
|
||||
audio_mixer_sound_t *sound;
|
||||
audio_mixer_stop_cb_t stop_cb;
|
||||
unsigned type;
|
||||
float volume;
|
||||
bool repeat;
|
||||
|
||||
};
|
||||
|
||||
static struct audio_mixer_voice s_voices[AUDIO_MIXER_MAX_VOICES] = {{0}};
|
||||
/* TODO/FIXME - static globals */
|
||||
static struct audio_mixer_voice s_voices[AUDIO_MIXER_MAX_VOICES] = {0};
|
||||
static unsigned s_rate = 0;
|
||||
|
||||
static bool wav2float(const rwav_t* wav, float** pcm, size_t samples_out)
|
||||
#ifdef HAVE_RWAV
|
||||
static bool wav_to_float(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 */
|
||||
@ -299,6 +303,7 @@ static bool one_shot_resample(const float* in, size_t samples_in,
|
||||
resampler->free(data);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void audio_mixer_init(unsigned rate)
|
||||
{
|
||||
@ -320,6 +325,7 @@ void audio_mixer_done(void)
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size)
|
||||
{
|
||||
#ifdef HAVE_RWAV
|
||||
/* WAV data */
|
||||
rwav_t wav;
|
||||
/* WAV samples converted to float */
|
||||
@ -334,7 +340,7 @@ audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size)
|
||||
|
||||
samples = wav.numsamples * 2;
|
||||
|
||||
if (!wav2float(&wav, &pcm, samples))
|
||||
if (!wav_to_float(&wav, &pcm, samples))
|
||||
return NULL;
|
||||
|
||||
if (wav.samplerate != s_rate)
|
||||
@ -364,6 +370,9 @@ audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size)
|
||||
rwav_free(&wav);
|
||||
|
||||
return sound;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size)
|
||||
@ -839,8 +848,8 @@ void audio_mixer_stop(audio_mixer_voice_t* voice)
|
||||
|
||||
if (voice)
|
||||
{
|
||||
stop_cb = voice->stop_cb;
|
||||
sound = voice->sound;
|
||||
stop_cb = voice->stop_cb;
|
||||
sound = voice->sound;
|
||||
|
||||
voice->type = AUDIO_MIXER_TYPE_NONE;
|
||||
|
||||
@ -899,8 +908,7 @@ static void audio_mixer_mix_ogg(float* buffer, size_t num_frames,
|
||||
float volume)
|
||||
{
|
||||
int i;
|
||||
struct resampler_data info = { 0 };
|
||||
float temp_buffer[AUDIO_MIXER_TEMP_BUFFER] = { 0 };
|
||||
float* temp_buffer = NULL;
|
||||
unsigned buf_free = (unsigned)(num_frames * 2);
|
||||
unsigned temp_samples = 0;
|
||||
float* pcm = NULL;
|
||||
@ -908,6 +916,9 @@ static void audio_mixer_mix_ogg(float* buffer, size_t num_frames,
|
||||
if (voice->types.ogg.position == voice->types.ogg.samples)
|
||||
{
|
||||
again:
|
||||
if (temp_buffer == NULL)
|
||||
temp_buffer = (float*)malloc(AUDIO_MIXER_TEMP_BUFFER * sizeof(float));
|
||||
|
||||
temp_samples = stb_vorbis_get_samples_float_interleaved(
|
||||
voice->types.ogg.stream, 2, temp_buffer,
|
||||
AUDIO_MIXER_TEMP_BUFFER) * 2;
|
||||
@ -922,28 +933,30 @@ again:
|
||||
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;
|
||||
}
|
||||
if (voice->stop_cb)
|
||||
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
|
||||
|
||||
voice->type = AUDIO_MIXER_TYPE_NONE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (voice->types.ogg.resampler)
|
||||
{
|
||||
voice->types.ogg.resampler->process(voice->types.ogg.resampler_data, &info);
|
||||
struct resampler_data info;
|
||||
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);
|
||||
}
|
||||
else
|
||||
memcpy(voice->types.ogg.buffer, temp_buffer, temp_samples * sizeof(float));
|
||||
memcpy(voice->types.ogg.buffer, temp_buffer,
|
||||
temp_samples * sizeof(float));
|
||||
|
||||
voice->types.ogg.position = 0;
|
||||
voice->types.ogg.samples = voice->types.ogg.buf_samples;
|
||||
}
|
||||
@ -958,15 +971,16 @@ again:
|
||||
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;
|
||||
}
|
||||
for (i = buf_free; i != 0; --i )
|
||||
*buffer++ += *pcm++ * volume;
|
||||
|
||||
voice->types.ogg.position += buf_free;
|
||||
voice->types.ogg.samples -= buf_free;
|
||||
|
||||
cleanup:
|
||||
if (temp_buffer != NULL)
|
||||
free(temp_buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1000,14 +1014,12 @@ again:
|
||||
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;
|
||||
}
|
||||
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;
|
||||
@ -1028,20 +1040,17 @@ again:
|
||||
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;
|
||||
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
|
||||
|
||||
@ -1051,11 +1060,11 @@ static void audio_mixer_mix_flac(float* buffer, size_t num_frames,
|
||||
float volume)
|
||||
{
|
||||
int i;
|
||||
struct resampler_data info = { 0 };
|
||||
struct resampler_data info;
|
||||
float temp_buffer[AUDIO_MIXER_TEMP_BUFFER] = { 0 };
|
||||
unsigned buf_free = (unsigned)(num_frames * 2);
|
||||
unsigned temp_samples = 0;
|
||||
float* pcm = NULL;
|
||||
float *pcm = NULL;
|
||||
|
||||
if (voice->types.flac.position == voice->types.flac.samples)
|
||||
{
|
||||
@ -1071,14 +1080,12 @@ again:
|
||||
drflac_seek_to_sample(voice->types.flac.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;
|
||||
}
|
||||
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;
|
||||
@ -1088,11 +1095,10 @@ again:
|
||||
info.ratio = voice->types.flac.ratio;
|
||||
|
||||
if (voice->types.flac.resampler)
|
||||
{
|
||||
voice->types.flac.resampler->process(voice->types.flac.resampler_data, &info);
|
||||
}
|
||||
voice->types.flac.resampler->process(
|
||||
voice->types.flac.resampler_data, &info);
|
||||
else
|
||||
memcpy(voice->types.flac.buffer, temp_buffer, temp_samples * sizeof(float));
|
||||
memcpy(voice->types.flac.buffer, temp_buffer, temp_samples * sizeof(float));
|
||||
voice->types.flac.position = 0;
|
||||
voice->types.flac.samples = voice->types.flac.buf_samples;
|
||||
}
|
||||
@ -1107,15 +1113,12 @@ again:
|
||||
buf_free -= voice->types.flac.samples;
|
||||
goto again;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for (i = buf_free; i != 0; --i )
|
||||
*buffer++ += *pcm++ * volume;
|
||||
|
||||
voice->types.flac.position += buf_free;
|
||||
voice->types.flac.samples -= buf_free;
|
||||
}
|
||||
for (i = buf_free; i != 0; --i )
|
||||
*buffer++ += *pcm++ * volume;
|
||||
|
||||
voice->types.flac.position += buf_free;
|
||||
voice->types.flac.samples -= buf_free;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1125,7 +1128,7 @@ static void audio_mixer_mix_mp3(float* buffer, size_t num_frames,
|
||||
float volume)
|
||||
{
|
||||
int i;
|
||||
struct resampler_data info = { 0 };
|
||||
struct resampler_data info;
|
||||
float temp_buffer[AUDIO_MIXER_TEMP_BUFFER] = { 0 };
|
||||
unsigned buf_free = (unsigned)(num_frames * 2);
|
||||
unsigned temp_samples = 0;
|
||||
@ -1134,7 +1137,9 @@ static void audio_mixer_mix_mp3(float* buffer, size_t num_frames,
|
||||
if (voice->types.mp3.position == voice->types.mp3.samples)
|
||||
{
|
||||
again:
|
||||
temp_samples = (unsigned)drmp3_read_f32(&voice->types.mp3.stream, AUDIO_MIXER_TEMP_BUFFER/2, temp_buffer) * 2;
|
||||
temp_samples = (unsigned)drmp3_read_f32(
|
||||
&voice->types.mp3.stream,
|
||||
AUDIO_MIXER_TEMP_BUFFER / 2, temp_buffer) * 2;
|
||||
|
||||
if (temp_samples == 0)
|
||||
{
|
||||
@ -1146,14 +1151,12 @@ again:
|
||||
drmp3_seek_to_frame(&voice->types.mp3.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;
|
||||
}
|
||||
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;
|
||||
@ -1163,9 +1166,11 @@ again:
|
||||
info.ratio = voice->types.mp3.ratio;
|
||||
|
||||
if (voice->types.mp3.resampler)
|
||||
voice->types.mp3.resampler->process(voice->types.mp3.resampler_data, &info);
|
||||
voice->types.mp3.resampler->process(
|
||||
voice->types.mp3.resampler_data, &info);
|
||||
else
|
||||
memcpy(voice->types.mp3.buffer, temp_buffer, temp_samples * sizeof(float));
|
||||
memcpy(voice->types.mp3.buffer, temp_buffer,
|
||||
temp_samples * sizeof(float));
|
||||
voice->types.mp3.position = 0;
|
||||
voice->types.mp3.samples = voice->types.mp3.buf_samples;
|
||||
}
|
||||
@ -1180,19 +1185,17 @@ again:
|
||||
buf_free -= voice->types.mp3.samples;
|
||||
goto again;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for (i = buf_free; i != 0; --i )
|
||||
*buffer++ += *pcm++ * volume;
|
||||
|
||||
voice->types.mp3.position += buf_free;
|
||||
voice->types.mp3.samples -= buf_free;
|
||||
}
|
||||
for (i = buf_free; i != 0; --i )
|
||||
*buffer++ += *pcm++ * volume;
|
||||
|
||||
voice->types.mp3.position += buf_free;
|
||||
voice->types.mp3.samples -= buf_free;
|
||||
}
|
||||
#endif
|
||||
|
||||
void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bool override)
|
||||
void audio_mixer_mix(float* buffer, size_t num_frames,
|
||||
float volume_override, bool override)
|
||||
{
|
||||
unsigned i;
|
||||
size_t j = 0;
|
||||
@ -1233,7 +1236,7 @@ void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bo
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0, sample = buffer; j < num_frames; j++, sample++)
|
||||
for (j = 0, sample = buffer; j < num_frames * 2; j++, sample++)
|
||||
{
|
||||
if (*sample < -1.0f)
|
||||
*sample = -1.0f;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (float_to_s16.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (float_to_s16_neon.S).
|
||||
@ -19,7 +19,7 @@
|
||||
* WHETHER IN AN ACTION 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(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||
|
||||
#ifndef __MACH__
|
||||
.arm
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (float_to_s16_neon.S).
|
||||
@ -19,7 +19,7 @@
|
||||
* WHETHER IN AN ACTION 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(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||
|
||||
#if defined(__thumb__)
|
||||
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (s16_to_float.c).
|
||||
@ -50,7 +50,7 @@ void convert_s16_float_asm(float *out, const int16_t *in,
|
||||
void convert_s16_to_float(float *out,
|
||||
const int16_t *in, size_t samples, float gain)
|
||||
{
|
||||
size_t i = 0;
|
||||
unsigned i = 0;
|
||||
|
||||
#if defined(__SSE2__)
|
||||
float fgain = gain / UINT32_C(0x80000000);
|
||||
@ -77,7 +77,6 @@ void convert_s16_to_float(float *out,
|
||||
* 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};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (s16_to_float_neon.S).
|
||||
@ -19,7 +19,7 @@
|
||||
* WHETHER IN AN ACTION 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(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||
|
||||
#ifndef __MACH__
|
||||
.arm
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (s16_to_float_neon.S).
|
||||
@ -19,7 +19,7 @@
|
||||
* WHETHER IN AN ACTION 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(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||
|
||||
#if defined(__thumb__)
|
||||
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
|
||||
|
2
externals/libretro-common/audio/dsp_filter.c
vendored
2
externals/libretro-common/audio/dsp_filter.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (dsp_filter.c).
|
||||
|
@ -8,17 +8,15 @@ INSTALLDIR := $(PREFIX)/lib/retroarch/filters/audio
|
||||
|
||||
ifeq ($(platform),)
|
||||
platform = unix
|
||||
ifeq ($(shell uname -a),)
|
||||
ifeq ($(shell uname -s),)
|
||||
platform = win
|
||||
else ifneq ($(findstring MINGW,$(shell uname -a)),)
|
||||
platform = win
|
||||
else ifneq ($(findstring Darwin,$(shell uname -a)),)
|
||||
else ifneq ($(findstring Darwin,$(shell uname -s)),)
|
||||
platform = osx
|
||||
arch = intel
|
||||
arch = intel
|
||||
ifeq ($(shell uname -p),powerpc)
|
||||
arch = ppc
|
||||
endif
|
||||
else ifneq ($(findstring win,$(shell uname -a)),)
|
||||
else ifneq ($(findstring MINGW,$(shell uname -s)),)
|
||||
platform = win
|
||||
endif
|
||||
endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (chorus.c).
|
||||
|
0
externals/libretro-common/audio/dsp_filters/configure
vendored
Normal file → Executable file
0
externals/libretro-common/audio/dsp_filters/configure
vendored
Normal file → Executable file
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (echo.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (echo.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (eq.c).
|
||||
@ -233,6 +233,7 @@ static void create_filter(struct eq_data *eq, unsigned size_log2,
|
||||
time_filter[i] *= window_mod * kaiser_window_function(phase, beta);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Debugging. */
|
||||
if (filter_path)
|
||||
{
|
||||
@ -244,6 +245,7 @@ static void create_filter(struct eq_data *eq, unsigned size_log2,
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Padded FFT to create our FFT filter.
|
||||
* Make our even-length filter odd by discarding the first coefficient.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (fft.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (fft.h).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (iir.c).
|
||||
@ -223,7 +223,8 @@ static void iir_filter_init(struct iir_data *iir,
|
||||
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];
|
||||
float b[3] = {0.0f};
|
||||
float a[3] = {0.0f};
|
||||
|
||||
if ((int)sample_rate == 44100)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (panning.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (phaser.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (reverb.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (tremolo.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (vibrato.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (wahwah.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_resampler.c).
|
||||
@ -28,12 +28,28 @@
|
||||
|
||||
#include <audio/audio_resampler.h>
|
||||
|
||||
static void resampler_null_process(void *a, struct resampler_data *b) { }
|
||||
static void resampler_null_free(void *a) { }
|
||||
static void *resampler_null_init(const struct resampler_config *a, double b,
|
||||
enum resampler_quality c, resampler_simd_mask_t d) { return (void*)0; }
|
||||
|
||||
retro_resampler_t null_resampler = {
|
||||
resampler_null_init,
|
||||
resampler_null_process,
|
||||
resampler_null_free,
|
||||
RESAMPLER_API_VERSION,
|
||||
"null",
|
||||
"null"
|
||||
};
|
||||
|
||||
static const retro_resampler_t *resampler_drivers[] = {
|
||||
&sinc_resampler,
|
||||
#ifdef HAVE_CC_RESAMPLER
|
||||
&CC_resampler,
|
||||
#endif
|
||||
#ifdef HAVE_NEAREST_RESAMPLER
|
||||
&nearest_resampler,
|
||||
#endif
|
||||
&null_resampler,
|
||||
NULL,
|
||||
};
|
||||
@ -66,36 +82,6 @@ static int find_resampler_driver_index(const char *ident)
|
||||
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.
|
||||
@ -140,6 +126,37 @@ static bool resampler_append_plugs(void **re,
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* audio_resampler_driver_find_handle:
|
||||
* @idx : index of driver to get handle to.
|
||||
*
|
||||
* Returns: handle to audio resampler driver at index. Can be NULL
|
||||
* if nothing found.
|
||||
**/
|
||||
const void *audio_resampler_driver_find_handle(int idx)
|
||||
{
|
||||
const void *drv = resampler_drivers[idx];
|
||||
if (!drv)
|
||||
return NULL;
|
||||
return drv;
|
||||
}
|
||||
|
||||
/**
|
||||
* audio_resampler_driver_find_ident:
|
||||
* @idx : index of driver to get handle to.
|
||||
*
|
||||
* Returns: Human-readable identifier of audio resampler driver at index.
|
||||
* Can be NULL if nothing found.
|
||||
**/
|
||||
const char *audio_resampler_driver_find_ident(int idx)
|
||||
{
|
||||
const retro_resampler_t *drv = resampler_drivers[idx];
|
||||
if (!drv)
|
||||
return NULL;
|
||||
return drv->ident;
|
||||
}
|
||||
|
||||
/**
|
||||
* retro_resampler_realloc:
|
||||
* @re : Resampler handle
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nearest_resampler.c).
|
||||
@ -40,11 +40,11 @@ static void resampler_nearest_process(
|
||||
audio_frame_float_t *outp = (audio_frame_float_t*)data->data_out;
|
||||
float ratio = 1.0 / data->ratio;
|
||||
|
||||
while(inp != inp_max)
|
||||
while (inp != inp_max)
|
||||
{
|
||||
while(re->fraction > 1)
|
||||
while (re->fraction > 1)
|
||||
{
|
||||
*outp++ = *inp;
|
||||
*outp++ = *inp;
|
||||
re->fraction -= ratio;
|
||||
}
|
||||
re->fraction++;
|
||||
@ -68,15 +68,9 @@ static void *resampler_nearest_init(const struct resampler_config *config,
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (sinc_resampler.c).
|
||||
@ -27,6 +27,7 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_environment.h>
|
||||
#include <retro_inline.h>
|
||||
#include <filters.h>
|
||||
#include <memalign.h>
|
||||
@ -67,6 +68,13 @@ enum sinc_window
|
||||
|
||||
typedef struct rarch_sinc_resampler
|
||||
{
|
||||
/* 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;
|
||||
unsigned enable_avx;
|
||||
unsigned phase_bits;
|
||||
unsigned subphase_bits;
|
||||
@ -77,14 +85,6 @@ typedef struct rarch_sinc_resampler
|
||||
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__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||
@ -129,19 +129,21 @@ static void resampler_sinc_process_neon(void *re_, struct resampler_data *data)
|
||||
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;
|
||||
while (resamp->time < phases)
|
||||
{
|
||||
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);
|
||||
process_sinc_neon_asm(output, buffer_l, buffer_r, phase_table, taps);
|
||||
|
||||
output += 2;
|
||||
out_frames++;
|
||||
resamp->time += ratio;
|
||||
output += 2;
|
||||
out_frames++;
|
||||
resamp->time += ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,87 +163,141 @@ static void resampler_sinc_process_avx(void *re_, struct resampler_data *data)
|
||||
size_t frames = data->input_frames;
|
||||
size_t out_frames = 0;
|
||||
|
||||
while (frames)
|
||||
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||
{
|
||||
while (frames && resamp->time >= phases)
|
||||
while (frames)
|
||||
{
|
||||
/* Push in reverse to make filter more obvious. */
|
||||
if (!resamp->ptr)
|
||||
resamp->ptr = resamp->taps;
|
||||
resamp->ptr--;
|
||||
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_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->buffer_r[resamp->ptr + resamp->taps] =
|
||||
resamp->buffer_r[resamp->ptr] = *input++;
|
||||
|
||||
resamp->time -= phases;
|
||||
frames--;
|
||||
resamp->time -= phases;
|
||||
frames--;
|
||||
}
|
||||
|
||||
{
|
||||
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||
unsigned taps = resamp->taps;
|
||||
while (resamp->time < phases)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||
|
||||
float *phase_table = resamp->phase_table + phase * taps * 2;
|
||||
float *delta_table = phase_table + taps;
|
||||
__m256 delta = _mm256_set1_ps((float)
|
||||
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod);
|
||||
|
||||
__m256 sum_l = _mm256_setzero_ps();
|
||||
__m256 sum_r = _mm256_setzero_ps();
|
||||
|
||||
for (i = 0; i < taps; i += 8)
|
||||
{
|
||||
__m256 buf_l = _mm256_loadu_ps(buffer_l + i);
|
||||
__m256 buf_r = _mm256_loadu_ps(buffer_r + i);
|
||||
__m256 deltas = _mm256_load_ps(delta_table + i);
|
||||
__m256 sinc = _mm256_add_ps(_mm256_load_ps((const float*)phase_table + i),
|
||||
_mm256_mul_ps(deltas, delta));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (resamp->time < phases)
|
||||
}
|
||||
else
|
||||
{
|
||||
while (frames)
|
||||
{
|
||||
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)
|
||||
while (frames && resamp->time >= phases)
|
||||
{
|
||||
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);
|
||||
/* 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--;
|
||||
}
|
||||
|
||||
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)
|
||||
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||
unsigned taps = resamp->taps;
|
||||
while (resamp->time < phases)
|
||||
{
|
||||
__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);
|
||||
}
|
||||
unsigned i;
|
||||
__m256 delta;
|
||||
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||
float *phase_table = resamp->phase_table + phase * taps;
|
||||
|
||||
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));
|
||||
__m256 sum_l = _mm256_setzero_ps();
|
||||
__m256 sum_r = _mm256_setzero_ps();
|
||||
|
||||
for (i = 0; i < taps; i += 8)
|
||||
{
|
||||
__m256 buf_l = _mm256_loadu_ps(buffer_l + i);
|
||||
__m256 buf_r = _mm256_loadu_ps(buffer_r + i);
|
||||
__m256 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,99 +317,161 @@ static void resampler_sinc_process_sse(void *re_, struct resampler_data *data)
|
||||
size_t frames = data->input_frames;
|
||||
size_t out_frames = 0;
|
||||
|
||||
while (frames)
|
||||
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||
{
|
||||
while (frames && resamp->time >= phases)
|
||||
while (frames)
|
||||
{
|
||||
/* Push in reverse to make filter more obvious. */
|
||||
if (!resamp->ptr)
|
||||
resamp->ptr = resamp->taps;
|
||||
resamp->ptr--;
|
||||
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_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->buffer_r[resamp->ptr + resamp->taps] =
|
||||
resamp->buffer_r[resamp->ptr] = *input++;
|
||||
|
||||
resamp->time -= phases;
|
||||
frames--;
|
||||
resamp->time -= phases;
|
||||
frames--;
|
||||
}
|
||||
|
||||
{
|
||||
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||
unsigned taps = resamp->taps;
|
||||
while (resamp->time < phases)
|
||||
{
|
||||
unsigned i;
|
||||
__m128 sum;
|
||||
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||
float *phase_table = resamp->phase_table + phase * taps * 2;
|
||||
float *delta_table = phase_table + taps;
|
||||
__m128 delta = _mm_set1_ps((float)
|
||||
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod);
|
||||
|
||||
__m128 sum_l = _mm_setzero_ps();
|
||||
__m128 sum_r = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < taps; i += 4)
|
||||
{
|
||||
__m128 buf_l = _mm_loadu_ps(buffer_l + i);
|
||||
__m128 buf_r = _mm_loadu_ps(buffer_r + i);
|
||||
__m128 deltas = _mm_load_ps(delta_table + i);
|
||||
__m128 _sinc = _mm_add_ps(_mm_load_ps((const float*)phase_table + i),
|
||||
_mm_mul_ps(deltas, delta));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (resamp->time < phases)
|
||||
}
|
||||
else
|
||||
{
|
||||
while (frames)
|
||||
{
|
||||
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;
|
||||
while (frames && resamp->time >= phases)
|
||||
{
|
||||
/* Push in reverse to make filter more obvious. */
|
||||
if (!resamp->ptr)
|
||||
resamp->ptr = resamp->taps;
|
||||
resamp->ptr--;
|
||||
|
||||
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;
|
||||
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--;
|
||||
}
|
||||
|
||||
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)
|
||||
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||
unsigned taps = resamp->taps;
|
||||
while (resamp->time < phases)
|
||||
{
|
||||
deltas = _mm_load_ps(delta_table + i);
|
||||
_sinc = _mm_add_ps(_mm_load_ps((const float*)phase_table + i),
|
||||
_mm_mul_ps(deltas, delta));
|
||||
unsigned i;
|
||||
__m128 sum;
|
||||
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||
float *phase_table = resamp->phase_table + phase * taps;
|
||||
|
||||
__m128 sum_l = _mm_setzero_ps();
|
||||
__m128 sum_r = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < taps; i += 4)
|
||||
{
|
||||
__m128 buf_l = _mm_loadu_ps(buffer_l + i);
|
||||
__m128 buf_r = _mm_loadu_ps(buffer_r + i);
|
||||
__m128 _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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,69 +490,112 @@ static void resampler_sinc_process_c(void *re_, struct resampler_data *data)
|
||||
size_t frames = data->input_frames;
|
||||
size_t out_frames = 0;
|
||||
|
||||
while (frames)
|
||||
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||
{
|
||||
while (frames && resamp->time >= phases)
|
||||
while (frames)
|
||||
{
|
||||
/* Push in reverse to make filter more obvious. */
|
||||
if (!resamp->ptr)
|
||||
resamp->ptr = resamp->taps;
|
||||
resamp->ptr--;
|
||||
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_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->buffer_r[resamp->ptr + resamp->taps] =
|
||||
resamp->buffer_r[resamp->ptr] = *input++;
|
||||
|
||||
resamp->time -= phases;
|
||||
frames--;
|
||||
}
|
||||
|
||||
{
|
||||
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||
unsigned taps = resamp->taps;
|
||||
while (resamp->time < phases)
|
||||
{
|
||||
unsigned i;
|
||||
float sum_l = 0.0f;
|
||||
float sum_r = 0.0f;
|
||||
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||
float *phase_table = resamp->phase_table + phase * taps * 2;
|
||||
float *delta_table = phase_table + taps;
|
||||
float delta = (float)
|
||||
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod;
|
||||
|
||||
for (i = 0; i < taps; i++)
|
||||
{
|
||||
float sinc_val = phase_table[i] + 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;
|
||||
}
|
||||
}
|
||||
|
||||
resamp->time -= phases;
|
||||
frames--;
|
||||
}
|
||||
|
||||
while (resamp->time < phases)
|
||||
}
|
||||
else
|
||||
{
|
||||
while (frames)
|
||||
{
|
||||
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;
|
||||
while (frames && resamp->time >= phases)
|
||||
{
|
||||
/* Push in reverse to make filter more obvious. */
|
||||
if (!resamp->ptr)
|
||||
resamp->ptr = resamp->taps;
|
||||
resamp->ptr--;
|
||||
|
||||
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;
|
||||
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--;
|
||||
}
|
||||
|
||||
for (i = 0; i < taps; i++)
|
||||
{
|
||||
float sinc_val = phase_table[i];
|
||||
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||
unsigned taps = resamp->taps;
|
||||
while (resamp->time < phases)
|
||||
{
|
||||
unsigned i;
|
||||
float sum_l = 0.0f;
|
||||
float sum_r = 0.0f;
|
||||
unsigned phase = resamp->time >> resamp->subphase_bits;
|
||||
float *phase_table = resamp->phase_table + phase * taps;
|
||||
|
||||
if (resamp->window_type == SINC_WINDOW_KAISER)
|
||||
sinc_val = sinc_val + delta_table[i] * delta;
|
||||
for (i = 0; i < taps; i++)
|
||||
{
|
||||
float sinc_val = phase_table[i];
|
||||
|
||||
sum_l += buffer_l[i] * sinc_val;
|
||||
sum_r += buffer_r[i] * sinc_val;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
output[0] = sum_l;
|
||||
output[1] = sum_r;
|
||||
|
||||
output += 2;
|
||||
out_frames++;
|
||||
resamp->time += ratio;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data->output_frames = out_frames;
|
||||
@ -506,7 +667,8 @@ static void sinc_init_table_kaiser(rarch_sinc_resampler_t *resamp,
|
||||
}
|
||||
}
|
||||
|
||||
static void sinc_init_table_lanczos(rarch_sinc_resampler_t *resamp, double cutoff,
|
||||
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;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (sinc_resampler_neon.S).
|
||||
@ -20,7 +20,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if defined(__ARM_NEON__)
|
||||
#if defined(__ARM_NEON__) && !defined(DONT_WANT_ARM_OPTIMIZATIONS)
|
||||
|
||||
#ifndef __MACH__
|
||||
.arm
|
||||
|
58
externals/libretro-common/cdrom/cdrom.c
vendored
58
externals/libretro-common/cdrom/cdrom.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2019 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (cdrom.c).
|
||||
@ -29,6 +29,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <compat/strl.h>
|
||||
#include <compat/strcasestr.h>
|
||||
#include <retro_math.h>
|
||||
#include <retro_timers.h>
|
||||
#include <streams/file_stream.h>
|
||||
@ -96,6 +97,7 @@ void increment_msf(unsigned char *min, unsigned char *sec, unsigned char *frame)
|
||||
*frame = (*frame < 74) ? (*frame + 1) : 0;
|
||||
}
|
||||
|
||||
#ifdef CDROM_DEBUG
|
||||
static void cdrom_print_sense_data(const unsigned char *sense, size_t len)
|
||||
{
|
||||
unsigned i;
|
||||
@ -252,6 +254,7 @@ static void cdrom_print_sense_data(const unsigned char *sense, size_t len)
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(_XBOX)
|
||||
static int cdrom_send_command_win32(const libretro_vfs_implementation_file *stream, CDROM_CMD_Direction dir, void *buf, size_t len, unsigned char *cmd, size_t cmd_len, unsigned char *sense, size_t sense_len)
|
||||
@ -519,7 +522,9 @@ retry:
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CDROM_DEBUG
|
||||
cdrom_print_sense_data(sense, sizeof(sense));
|
||||
#endif
|
||||
|
||||
/* INQUIRY/TEST/SENSE should never fail, don't retry. */
|
||||
/* READ ATIP seems to fail outright on some drives with pressed discs, skip retries. */
|
||||
@ -672,7 +677,9 @@ int cdrom_get_sense(libretro_vfs_implementation_file *stream, unsigned char *sen
|
||||
if (rv)
|
||||
return 1;
|
||||
|
||||
#ifdef CDROM_DEBUG
|
||||
cdrom_print_sense_data(buf, sizeof(buf));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1336,22 +1343,26 @@ struct string_list* cdrom_get_available_drives(void)
|
||||
|
||||
for (i = 0; i < (int)dir_list->size; i++)
|
||||
{
|
||||
if (strstr(dir_list->elems[i].data, "/dev/sg"))
|
||||
if (string_starts_with_size(dir_list->elems[i].data, "/dev/sg",
|
||||
STRLEN_CONST("/dev/sg")))
|
||||
{
|
||||
char drive_model[32] = {0};
|
||||
char drive_string[33] = {0};
|
||||
union string_list_elem_attr attr = {0};
|
||||
int dev_index = 0;
|
||||
RFILE *file = filestream_open(dir_list->elems[i].data, RETRO_VFS_FILE_ACCESS_READ, 0);
|
||||
libretro_vfs_implementation_file *stream;
|
||||
bool is_cdrom = false;
|
||||
char drive_model[32] = {0};
|
||||
char drive_string[33] = {0};
|
||||
union string_list_elem_attr attr = {0};
|
||||
int dev_index = 0;
|
||||
RFILE *file = filestream_open(
|
||||
dir_list->elems[i].data, RETRO_VFS_FILE_ACCESS_READ, 0);
|
||||
bool is_cdrom = false;
|
||||
|
||||
found = true;
|
||||
|
||||
if (!file)
|
||||
{
|
||||
#ifdef CDROM_DEBUG
|
||||
printf("[CDROM] Could not open %s, please check permissions.\n", dir_list->elems[i].data);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1362,10 +1373,11 @@ struct string_list* cdrom_get_available_drives(void)
|
||||
if (!is_cdrom)
|
||||
continue;
|
||||
|
||||
sscanf(dir_list->elems[i].data + strlen("/dev/sg"), "%d", &dev_index);
|
||||
sscanf(dir_list->elems[i].data + STRLEN_CONST("/dev/sg"),
|
||||
"%d", &dev_index);
|
||||
|
||||
dev_index = '0' + dev_index;
|
||||
attr.i = dev_index;
|
||||
attr.i = dev_index;
|
||||
|
||||
if (!string_is_empty(drive_model))
|
||||
strlcat(drive_string, drive_model, sizeof(drive_string));
|
||||
@ -1378,29 +1390,34 @@ struct string_list* cdrom_get_available_drives(void)
|
||||
|
||||
if (!found)
|
||||
{
|
||||
char *buf = NULL;
|
||||
char *buf = NULL;
|
||||
int64_t len = 0;
|
||||
|
||||
if (filestream_read_file("/proc/modules", (void**)&buf, &len))
|
||||
{
|
||||
struct string_list *mods = string_split(buf, "\n");
|
||||
bool found = false;
|
||||
#ifdef CDROM_DEBUG
|
||||
bool found = false;
|
||||
#endif
|
||||
struct string_list mods = {0};
|
||||
|
||||
if (mods)
|
||||
string_list_initialize(&mods);
|
||||
|
||||
if (string_split_noalloc(&mods, buf, "\n"))
|
||||
{
|
||||
|
||||
for (i = 0; i < mods->size; i++)
|
||||
for (i = 0; i < mods.size; i++)
|
||||
{
|
||||
if (strcasestr(mods->elems[i].data, "sg "))
|
||||
if (strcasestr(mods.elems[i].data, "sg "))
|
||||
{
|
||||
#ifdef CDROM_DEBUG
|
||||
found = true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string_list_free(mods);
|
||||
}
|
||||
string_list_deinitialize(&mods);
|
||||
|
||||
#ifdef CDROM_DEBUG
|
||||
if (found)
|
||||
{
|
||||
printf("[CDROM] No sg devices found but kernel module is loaded.\n");
|
||||
@ -1411,12 +1428,15 @@ struct string_list* cdrom_get_available_drives(void)
|
||||
printf("[CDROM] No sg devices found and sg kernel module is not loaded.\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef CDROM_DEBUG
|
||||
else
|
||||
{
|
||||
printf("[CDROM] No sg devices found, could not check if sg kernel module is loaded.\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
string_list_free(dir_list);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_fnmatch.c).
|
||||
@ -20,9 +20,6 @@
|
||||
* 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>
|
||||
@ -123,37 +120,3 @@ int rl_fnmatch(const char *pattern, const char *string, int flags)
|
||||
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_getopt.c).
|
||||
|
237
externals/libretro-common/compat/compat_ifaddrs.c
vendored
237
externals/libretro-common/compat/compat_ifaddrs.c
vendored
@ -48,12 +48,13 @@ static int netlink_socket(void)
|
||||
struct sockaddr_nl l_addr;
|
||||
int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
|
||||
if(l_socket < 0)
|
||||
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)
|
||||
|
||||
if (bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
|
||||
{
|
||||
close(l_socket);
|
||||
return -1;
|
||||
@ -91,67 +92,77 @@ static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
|
||||
struct iovec l_iov = { p_buffer, p_len };
|
||||
struct sockaddr_nl l_addr;
|
||||
|
||||
for(;;)
|
||||
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);
|
||||
int l_result;
|
||||
|
||||
if(l_result < 0)
|
||||
l_msg.msg_name = (void *)&l_addr;
|
||||
l_msg.msg_namelen = sizeof(l_addr);
|
||||
l_msg.msg_iov = &l_iov;
|
||||
l_msg.msg_iovlen = 1;
|
||||
l_msg.msg_control = NULL;
|
||||
l_msg.msg_controllen = 0;
|
||||
l_msg.msg_flags = 0;
|
||||
|
||||
l_result = recvmsg(p_socket, &l_msg, 0);
|
||||
|
||||
if (l_result < 0)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(l_msg.msg_flags & MSG_TRUNC) /* buffer too small */
|
||||
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)
|
||||
static struct nlmsghdr *getNetlinkResponse(int p_socket,
|
||||
int *p_size, int *p_done)
|
||||
{
|
||||
size_t l_size = 4096;
|
||||
size_t l_size = 4096;
|
||||
void *l_buffer = NULL;
|
||||
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
int l_read;
|
||||
|
||||
free(l_buffer);
|
||||
l_buffer = malloc(l_size);
|
||||
if (l_buffer == NULL)
|
||||
if (!l_buffer)
|
||||
return NULL;
|
||||
|
||||
int l_read = netlink_recv(p_socket, l_buffer, l_size);
|
||||
l_read = netlink_recv(p_socket, l_buffer, l_size);
|
||||
*p_size = l_read;
|
||||
|
||||
if(l_read == -2)
|
||||
if (l_read == -2)
|
||||
{
|
||||
free(l_buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(l_read >= 0)
|
||||
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))
|
||||
|
||||
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)
|
||||
if ( (pid_t)l_hdr->nlmsg_pid != l_pid ||
|
||||
(int)l_hdr->nlmsg_seq != p_socket)
|
||||
continue;
|
||||
|
||||
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
if (l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
*p_done = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if(l_hdr->nlmsg_type == NLMSG_ERROR)
|
||||
if (l_hdr->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
free(l_buffer);
|
||||
return NULL;
|
||||
@ -166,8 +177,8 @@ static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_don
|
||||
|
||||
static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
|
||||
{
|
||||
NetlinkList *l_item = malloc(sizeof(NetlinkList));
|
||||
if (l_item == NULL)
|
||||
NetlinkList *l_item = (NetlinkList*)malloc(sizeof(NetlinkList));
|
||||
if (!l_item)
|
||||
return NULL;
|
||||
|
||||
l_item->m_next = NULL;
|
||||
@ -179,7 +190,8 @@ static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
|
||||
static void freeResultList(NetlinkList *p_list)
|
||||
{
|
||||
NetlinkList *l_cur;
|
||||
while(p_list)
|
||||
|
||||
while (p_list)
|
||||
{
|
||||
l_cur = p_list;
|
||||
p_list = p_list->m_next;
|
||||
@ -190,29 +202,30 @@ static void freeResultList(NetlinkList *p_list)
|
||||
|
||||
static NetlinkList *getResultList(int p_socket, int p_request)
|
||||
{
|
||||
if(netlink_send(p_socket, p_request) < 0)
|
||||
int l_size;
|
||||
NetlinkList *l_list = NULL;
|
||||
NetlinkList *l_end = NULL;
|
||||
int l_done = 0;
|
||||
|
||||
if (netlink_send(p_socket, p_request) < 0)
|
||||
return NULL;
|
||||
|
||||
NetlinkList *l_list = NULL;
|
||||
NetlinkList *l_end = NULL;
|
||||
int l_size;
|
||||
int l_done = 0;
|
||||
while(!l_done)
|
||||
while (!l_done)
|
||||
{
|
||||
NetlinkList *l_item = NULL;
|
||||
struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
|
||||
if(!l_hdr)
|
||||
if (!l_hdr)
|
||||
goto error;
|
||||
|
||||
l_item = newListItem(l_hdr, l_size);
|
||||
if (!l_item)
|
||||
goto error;
|
||||
|
||||
if(!l_list)
|
||||
l_list = l_item;
|
||||
if (!l_list)
|
||||
l_list = l_item;
|
||||
else
|
||||
l_end->m_next = l_item;
|
||||
l_end = l_item;
|
||||
l_end = l_item;
|
||||
}
|
||||
|
||||
return l_list;
|
||||
@ -267,12 +280,12 @@ static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_
|
||||
|
||||
static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
|
||||
{
|
||||
if(!*p_resultList)
|
||||
if (!*p_resultList)
|
||||
*p_resultList = p_entry;
|
||||
else
|
||||
{
|
||||
struct ifaddrs *l_cur = *p_resultList;
|
||||
while(l_cur->ifa_next)
|
||||
while (l_cur->ifa_next)
|
||||
l_cur = l_cur->ifa_next;
|
||||
l_cur->ifa_next = p_entry;
|
||||
}
|
||||
@ -288,7 +301,8 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
|
||||
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))
|
||||
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)
|
||||
@ -308,8 +322,8 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
|
||||
}
|
||||
}
|
||||
|
||||
l_entry = malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
|
||||
if (l_entry == NULL)
|
||||
l_entry = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
|
||||
if (!l_entry)
|
||||
return -1;
|
||||
|
||||
memset(l_entry, 0, sizeof(struct ifaddrs));
|
||||
@ -320,16 +334,20 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
|
||||
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.
|
||||
/* 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))
|
||||
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);
|
||||
void *l_rtaData = RTA_DATA(l_rta);
|
||||
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||
|
||||
switch(l_rta->rta_type)
|
||||
{
|
||||
case IFLA_ADDRESS:
|
||||
@ -339,8 +357,8 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
|
||||
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;
|
||||
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);
|
||||
@ -364,17 +382,19 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks)
|
||||
static struct ifaddrs *findInterface(int p_index,
|
||||
struct ifaddrs **p_links, int p_numLinks)
|
||||
{
|
||||
int l_num = 0;
|
||||
int l_num = 0;
|
||||
struct ifaddrs *l_cur = *p_links;
|
||||
while(l_cur && l_num < p_numLinks)
|
||||
|
||||
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)
|
||||
if (l_index == p_index)
|
||||
return l_cur;
|
||||
|
||||
l_cur = l_cur->ifa_next;
|
||||
@ -383,21 +403,24 @@ static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks)
|
||||
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 rtattr *l_rta;
|
||||
size_t l_rtaSize;
|
||||
size_t l_nameSize = 0;
|
||||
size_t l_addrSize = 0;
|
||||
int l_addedNetmask = 0;
|
||||
struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
|
||||
struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
|
||||
|
||||
if(l_info->ifa_family == AF_PACKET)
|
||||
if (l_info->ifa_family == AF_PACKET)
|
||||
return 0;
|
||||
|
||||
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
|
||||
struct rtattr *l_rta;
|
||||
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))
|
||||
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);
|
||||
|
||||
@ -405,7 +428,7 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList,
|
||||
{
|
||||
case IFA_ADDRESS:
|
||||
case IFA_LOCAL:
|
||||
if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
|
||||
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));
|
||||
@ -422,8 +445,8 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList,
|
||||
}
|
||||
}
|
||||
|
||||
struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
|
||||
if (l_entry == NULL)
|
||||
struct ifaddrs *l_entry = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
|
||||
if (!l_entry)
|
||||
return -1;
|
||||
|
||||
memset(l_entry, 0, sizeof(struct ifaddrs));
|
||||
@ -433,11 +456,12 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList,
|
||||
char *l_addr = l_name + l_nameSize;
|
||||
|
||||
l_entry->ifa_flags = l_info->ifa_flags;
|
||||
if(l_interface)
|
||||
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))
|
||||
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);
|
||||
@ -449,24 +473,24 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList,
|
||||
{
|
||||
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 (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))
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
else if (l_rta->rta_type == IFA_LOCAL)
|
||||
{
|
||||
if(l_entry->ifa_addr)
|
||||
if (l_entry->ifa_addr)
|
||||
l_entry->ifa_dstaddr = l_entry->ifa_addr;
|
||||
l_entry->ifa_addr = (struct sockaddr *)l_addr;
|
||||
}
|
||||
@ -485,7 +509,7 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList,
|
||||
}
|
||||
}
|
||||
|
||||
if(l_entry->ifa_addr &&
|
||||
if (l_entry->ifa_addr &&
|
||||
( l_entry->ifa_addr->sa_family == AF_INET
|
||||
|| l_entry->ifa_addr->sa_family == AF_INET6))
|
||||
{
|
||||
@ -498,9 +522,9 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList,
|
||||
|
||||
l_mask[0] = '\0';
|
||||
|
||||
for(i=0; i<(l_prefix/8); ++i)
|
||||
for (i=0; i<(l_prefix/8); ++i)
|
||||
l_mask[i] = 0xff;
|
||||
if(l_prefix % 8)
|
||||
if (l_prefix % 8)
|
||||
l_mask[i] = 0xff << (8 - (l_prefix % 8));
|
||||
|
||||
makeSockaddr(l_entry->ifa_addr->sa_family,
|
||||
@ -516,23 +540,26 @@ 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)
|
||||
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))
|
||||
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)
|
||||
if ( (pid_t)l_hdr->nlmsg_pid != l_pid ||
|
||||
(int)l_hdr->nlmsg_seq != p_socket)
|
||||
continue;
|
||||
|
||||
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
if (l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
break;
|
||||
|
||||
if(l_hdr->nlmsg_type == RTM_NEWLINK)
|
||||
if (l_hdr->nlmsg_type == RTM_NEWLINK)
|
||||
{
|
||||
if(interpretLink(l_hdr, p_resultList) == -1)
|
||||
if (interpretLink(l_hdr, p_resultList) == -1)
|
||||
return -1;
|
||||
++l_numLinks;
|
||||
}
|
||||
@ -545,20 +572,22 @@ 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)
|
||||
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))
|
||||
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)
|
||||
if ( (pid_t)l_hdr->nlmsg_pid != l_pid
|
||||
|| (int)l_hdr->nlmsg_seq != p_socket)
|
||||
continue;
|
||||
|
||||
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
if (l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
break;
|
||||
|
||||
if(l_hdr->nlmsg_type == RTM_NEWADDR)
|
||||
if (l_hdr->nlmsg_type == RTM_NEWADDR)
|
||||
{
|
||||
if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
|
||||
return -1;
|
||||
@ -570,34 +599,40 @@ static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList,
|
||||
|
||||
int getifaddrs(struct ifaddrs **ifap)
|
||||
{
|
||||
int l_socket = 0;
|
||||
int l_result = 0;
|
||||
if(!ifap)
|
||||
NetlinkList *l_linkResults;
|
||||
NetlinkList *l_addrResults;
|
||||
int l_numLinks;
|
||||
int l_socket = 0;
|
||||
int l_result = 0;
|
||||
if (!ifap)
|
||||
return -1;
|
||||
*ifap = NULL;
|
||||
|
||||
*ifap = NULL;
|
||||
|
||||
l_socket = netlink_socket();
|
||||
if(l_socket < 0)
|
||||
|
||||
if (l_socket < 0)
|
||||
return -1;
|
||||
|
||||
NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK);
|
||||
if(!l_linkResults)
|
||||
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)
|
||||
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);
|
||||
l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
|
||||
|
||||
if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
|
||||
if ( l_numLinks == -1 ||
|
||||
interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
|
||||
l_result = -1;
|
||||
|
||||
freeResultList(l_linkResults);
|
||||
@ -610,10 +645,10 @@ void freeifaddrs(struct ifaddrs *ifa)
|
||||
{
|
||||
struct ifaddrs *l_cur = NULL;
|
||||
|
||||
while(ifa)
|
||||
while (ifa)
|
||||
{
|
||||
l_cur = ifa;
|
||||
ifa = ifa->ifa_next;
|
||||
ifa = ifa->ifa_next;
|
||||
free(l_cur);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_posix_string.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_snprintf.c).
|
||||
@ -33,12 +33,12 @@
|
||||
#if _MSC_VER < 1300
|
||||
#define _vscprintf c89_vscprintf_retro__
|
||||
|
||||
static int c89_vscprintf_retro__(const char *format, va_list pargs)
|
||||
static int c89_vscprintf_retro__(const char *fmt, va_list pargs)
|
||||
{
|
||||
int retval;
|
||||
va_list argcopy;
|
||||
va_copy(argcopy, pargs);
|
||||
retval = vsnprintf(NULL, 0, format, argcopy);
|
||||
retval = vsnprintf(NULL, 0, fmt, argcopy);
|
||||
va_end(argcopy);
|
||||
return retval;
|
||||
}
|
||||
@ -46,38 +46,36 @@ static int c89_vscprintf_retro__(const char *format, va_list pargs)
|
||||
|
||||
/* 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 c99_vsnprintf_retro__(char *s, size_t len, const char *fmt, va_list ap)
|
||||
{
|
||||
int count = -1;
|
||||
|
||||
if (size != 0)
|
||||
if (len != 0)
|
||||
{
|
||||
#if (_MSC_VER <= 1310)
|
||||
count = _vsnprintf(outBuf, size - 1, format, ap);
|
||||
count = _vsnprintf(s, len - 1, fmt, ap);
|
||||
#else
|
||||
count = _vsnprintf_s(outBuf, size, size - 1, format, ap);
|
||||
count = _vsnprintf_s(s, len, len - 1, fmt, ap);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (count == -1)
|
||||
count = _vscprintf(format, ap);
|
||||
count = _vscprintf(fmt, ap);
|
||||
|
||||
if (count == size)
|
||||
{
|
||||
/* there was no room for a NULL, so truncate the last character */
|
||||
outBuf[size - 1] = '\0';
|
||||
}
|
||||
/* there was no room for a NULL, so truncate the last character */
|
||||
if (count == len && len)
|
||||
s[len - 1] = '\0';
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...)
|
||||
int c99_snprintf_retro__(char *s, size_t len, const char *fmt, ...)
|
||||
{
|
||||
int count;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
count = c99_vsnprintf_retro__(outBuf, size, format, ap);
|
||||
va_start(ap, fmt);
|
||||
count = c99_vsnprintf_retro__(s, len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return count;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_strcasestr.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_strl.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2017 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (compat_snprintf.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (fopen_utf8.c).
|
||||
|
35
externals/libretro-common/dynamic/dylib.c
vendored
35
externals/libretro-common/dynamic/dylib.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (dylib.c).
|
||||
@ -78,35 +78,36 @@ dylib_t dylib_load(const char *path)
|
||||
int prevmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
||||
#endif
|
||||
#ifdef __WINRT__
|
||||
dylib_t lib;
|
||||
/* On UWP, you can only load DLLs inside your install directory, using a special function that takes a relative path */
|
||||
char relative_path_abbrev[PATH_MAX_LENGTH];
|
||||
char *relative_path = relative_path_abbrev;
|
||||
wchar_t *path_wide = NULL;
|
||||
|
||||
relative_path_abbrev[0] = '\0';
|
||||
|
||||
if (!path_is_absolute(path))
|
||||
RARCH_WARN("Relative path in dylib_load! This is likely an attempt to load a system library that will fail\n");
|
||||
|
||||
char *relative_path_abbrev = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
|
||||
fill_pathname_abbreviate_special(relative_path_abbrev, path, PATH_MAX_LENGTH * sizeof(char));
|
||||
fill_pathname_abbreviate_special(relative_path_abbrev, path, sizeof(relative_path_abbrev));
|
||||
|
||||
char *relative_path = relative_path_abbrev;
|
||||
if (relative_path[0] != ':' || !path_char_is_slash(relative_path[1]))
|
||||
{
|
||||
/* Path to dylib_load is not inside app install directory.
|
||||
* Loading will probably fail. */
|
||||
}
|
||||
/* Path to dylib_load is not inside app install directory.
|
||||
* Loading will probably fail. */
|
||||
if (relative_path[0] != ':' || !PATH_CHAR_IS_SLASH(relative_path[1])) { }
|
||||
else
|
||||
relative_path += 2;
|
||||
|
||||
|
||||
wchar_t *pathW = utf8_to_utf16_string_alloc(relative_path);
|
||||
dylib_t lib = LoadPackagedLibrary(pathW, 0);
|
||||
free(pathW);
|
||||
path_wide = utf8_to_utf16_string_alloc(relative_path);
|
||||
lib = LoadPackagedLibrary(path_wide, 0);
|
||||
free(path_wide);
|
||||
|
||||
free(relative_path_abbrev);
|
||||
#elif defined(LEGACY_WIN32)
|
||||
dylib_t lib = LoadLibrary(path);
|
||||
dylib_t lib = LoadLibrary(path);
|
||||
#else
|
||||
wchar_t *pathW = utf8_to_utf16_string_alloc(path);
|
||||
dylib_t lib = LoadLibraryW(pathW);
|
||||
free(pathW);
|
||||
wchar_t *path_wide = utf8_to_utf16_string_alloc(path);
|
||||
dylib_t lib = LoadLibraryW(path_wide);
|
||||
free(path_wide);
|
||||
#endif
|
||||
|
||||
#ifndef __WINRT__
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (encoding_crc32.c).
|
||||
@ -117,7 +117,7 @@ uint32_t file_crc32(uint32_t crc, const char *path)
|
||||
if (!buf)
|
||||
goto error;
|
||||
|
||||
for(i = 0; i < CRC32_MAX_MB; i++)
|
||||
for (i = 0; i < CRC32_MAX_MB; i++)
|
||||
{
|
||||
int64_t nread = filestream_read(file, buf, CRC32_BUFFER_SIZE);
|
||||
if (nread < 0)
|
||||
|
146
externals/libretro-common/encodings/encoding_utf.c
vendored
146
externals/libretro-common/encodings/encoding_utf.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (encoding_utf.c).
|
||||
@ -37,6 +37,8 @@
|
||||
#include <xtl.h>
|
||||
#endif
|
||||
|
||||
#define UTF8_WALKBYTE(string) (*((*(string))++))
|
||||
|
||||
static unsigned leading_ones(uint8_t c)
|
||||
{
|
||||
unsigned ones = 0;
|
||||
@ -89,13 +91,14 @@ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
||||
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;
|
||||
size_t out_pos = 0;
|
||||
size_t in_pos = 0;
|
||||
static const
|
||||
uint8_t utf8_limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unsigned numAdds;
|
||||
unsigned num_adds;
|
||||
uint32_t value;
|
||||
|
||||
if (in_pos == in_size)
|
||||
@ -124,21 +127,21 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||
value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
|
||||
}
|
||||
|
||||
for (numAdds = 1; numAdds < 5; numAdds++)
|
||||
if (value < (((uint32_t)1) << (numAdds * 5 + 6)))
|
||||
for (num_adds = 1; num_adds < 5; num_adds++)
|
||||
if (value < (((uint32_t)1) << (num_adds * 5 + 6)))
|
||||
break;
|
||||
if (out)
|
||||
out[out_pos] = (char)(kUtf8Limits[numAdds - 1]
|
||||
+ (value >> (6 * numAdds)));
|
||||
out[out_pos] = (char)(utf8_limits[num_adds - 1]
|
||||
+ (value >> (6 * num_adds)));
|
||||
out_pos++;
|
||||
do
|
||||
{
|
||||
numAdds--;
|
||||
num_adds--;
|
||||
if (out)
|
||||
out[out_pos] = (char)(0x80
|
||||
+ ((value >> (6 * numAdds)) & 0x3F));
|
||||
+ ((value >> (6 * num_adds)) & 0x3F));
|
||||
out_pos++;
|
||||
}while (numAdds != 0);
|
||||
}while (num_adds != 0);
|
||||
}
|
||||
|
||||
*out_chars = out_pos;
|
||||
@ -166,13 +169,15 @@ size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
|
||||
while (*sb && chars-- > 0)
|
||||
{
|
||||
sb++;
|
||||
while ((*sb & 0xC0) == 0x80) 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--;
|
||||
while ((*sb & 0xC0) == 0x80)
|
||||
sb--;
|
||||
}
|
||||
|
||||
memcpy(d, sb_org, sb-sb_org);
|
||||
@ -184,14 +189,18 @@ size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
|
||||
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++;
|
||||
while ((*strb & 0xC0)==0x80)
|
||||
strb++;
|
||||
chars--;
|
||||
} while(chars);
|
||||
}while (chars);
|
||||
|
||||
return (const char*)strb;
|
||||
}
|
||||
|
||||
@ -211,24 +220,22 @@ size_t utf8len(const char *string)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define utf8_walkbyte(string) (*((*(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);
|
||||
uint8_t first = UTF8_WALKBYTE(string);
|
||||
uint32_t ret = 0;
|
||||
|
||||
if (first < 128)
|
||||
return first;
|
||||
|
||||
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
|
||||
ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F);
|
||||
if (first >= 0xE0)
|
||||
{
|
||||
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
|
||||
ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F);
|
||||
if (first >= 0xF0)
|
||||
{
|
||||
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
|
||||
ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F);
|
||||
return ret | (first & 7) << 18;
|
||||
}
|
||||
return ret | (first & 15) << 12;
|
||||
@ -277,9 +284,7 @@ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
|
||||
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 = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0);
|
||||
|
||||
/* Windows 95 will return 0 from these functions with
|
||||
@ -292,54 +297,51 @@ static char *mb_to_mb_string_alloc(const char *str,
|
||||
* MultiByteToWideChar also supports CP_UTF7 and CP_UTF8.
|
||||
*/
|
||||
|
||||
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
|
||||
if (!path_buf_wide_len)
|
||||
return strdup(str);
|
||||
|
||||
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)
|
||||
{
|
||||
int path_buf_len = WideCharToMultiByte(cp_out, 0,
|
||||
path_buf_wide, -1, NULL, 0, NULL, NULL);
|
||||
|
||||
if (path_buf_len)
|
||||
{
|
||||
char *path_buf = (char*)
|
||||
calloc(path_buf_len + sizeof(char), sizeof(char));
|
||||
|
||||
if (path_buf)
|
||||
{
|
||||
WideCharToMultiByte(cp_out, 0,
|
||||
path_buf_wide, -1, path_buf,
|
||||
path_buf_len, NULL, NULL);
|
||||
|
||||
free(path_buf_wide);
|
||||
|
||||
if (*path_buf)
|
||||
return path_buf;
|
||||
|
||||
free(path_buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free(path_buf_wide);
|
||||
return strdup(str);
|
||||
}
|
||||
}
|
||||
|
||||
free(path_buf_wide);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -379,13 +381,13 @@ char* local_to_utf8_string_alloc(const char *str)
|
||||
wchar_t* utf8_to_utf16_string_alloc(const char *str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int len = 0;
|
||||
int out_len = 0;
|
||||
int len = 0;
|
||||
int out_len = 0;
|
||||
#else
|
||||
size_t len = 0;
|
||||
size_t len = 0;
|
||||
size_t out_len = 0;
|
||||
#endif
|
||||
wchar_t *buf = NULL;
|
||||
wchar_t *buf = NULL;
|
||||
|
||||
if (!str || !*str)
|
||||
return NULL;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (features_cpu.c).
|
||||
@ -39,6 +39,10 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __PSL1GHT__
|
||||
#include <lv2/systime.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#ifndef _PPU_INTRINSICS_H
|
||||
#include <ppu_intrinsics.h>
|
||||
@ -66,9 +70,7 @@
|
||||
#endif
|
||||
|
||||
#if defined(PS2)
|
||||
#include <kernel.h>
|
||||
#include <timer.h>
|
||||
#include <time.h>
|
||||
#include <ps2sdkapi.h>
|
||||
#endif
|
||||
|
||||
#if defined(__PSL1GHT__)
|
||||
@ -167,7 +169,7 @@ retro_perf_tick_t cpu_features_get_perf_counter(void)
|
||||
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__)
|
||||
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__)
|
||||
struct timespec tv = {0};
|
||||
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) == 0)
|
||||
time_ticks = (retro_perf_tick_t)tv.tv_sec * 1000000000 +
|
||||
@ -181,26 +183,22 @@ retro_perf_tick_t cpu_features_get_perf_counter(void)
|
||||
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__)
|
||||
#elif defined(__CELLOS_LV2__) || defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__) || defined(__PSL1GHT__)
|
||||
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(PSP) || defined(VITA)
|
||||
time_ticks = sceKernelGetSystemTimeWide();
|
||||
#elif defined(PS2)
|
||||
time_ticks = clock()*294912; // 294,912MHZ / 1000 msecs
|
||||
time_ticks = ps2_clock();
|
||||
#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);
|
||||
#elif defined(HAVE_LIBNX)
|
||||
time_ticks = armGetSystemTick();
|
||||
#elif defined(EMSCRIPTEN)
|
||||
time_ticks = emscripten_get_now() * 1000;
|
||||
#endif
|
||||
|
||||
return time_ticks;
|
||||
@ -225,7 +223,9 @@ retro_time_t cpu_features_get_time_usec(void)
|
||||
|
||||
if (!QueryPerformanceCounter(&count))
|
||||
return 0;
|
||||
return count.QuadPart * 1000000 / freq.QuadPart;
|
||||
return (count.QuadPart / freq.QuadPart * 1000000) + (count.QuadPart % freq.QuadPart * 1000000 / freq.QuadPart);
|
||||
#elif defined(__PSL1GHT__)
|
||||
return sysGetSystemTime();
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
return sys_time_get_system_time();
|
||||
#elif defined(GEKKO)
|
||||
@ -234,7 +234,9 @@ retro_time_t cpu_features_get_time_usec(void)
|
||||
return ticks_to_us(OSGetSystemTime());
|
||||
#elif defined(SWITCH) || defined(HAVE_LIBNX)
|
||||
return (svcGetSystemTick() * 10) / 192;
|
||||
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__)
|
||||
#elif defined(_3DS)
|
||||
return osGetTime() * 1000;
|
||||
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__) || defined(DJGPP)
|
||||
struct timespec tv = {0};
|
||||
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) < 0)
|
||||
return 0;
|
||||
@ -242,15 +244,9 @@ retro_time_t cpu_features_get_time_usec(void)
|
||||
#elif defined(EMSCRIPTEN)
|
||||
return emscripten_get_now() * 1000;
|
||||
#elif defined(PS2)
|
||||
return clock()*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();
|
||||
return ps2_clock() / PS2_CLOCKS_PER_MSEC * 1000;
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
return sceKernelGetSystemTimeWide();
|
||||
#else
|
||||
#error "Your platform does not have a timer function implemented in cpu_features_get_time_usec(). Cannot continue."
|
||||
#endif
|
||||
@ -289,7 +285,9 @@ void x86_cpuid(int func, int flags[4])
|
||||
#elif defined(_MSC_VER)
|
||||
__cpuid(flags, func);
|
||||
#else
|
||||
#ifndef NDEBUG
|
||||
printf("Unknown compiler. Cannot check CPUID with inline assembly.\n");
|
||||
#endif
|
||||
memset(flags, 0, 4 * sizeof(int));
|
||||
#endif
|
||||
}
|
||||
@ -311,7 +309,9 @@ static uint64_t xgetbv_x86(uint32_t idx)
|
||||
/* Intrinsic only works on 2010 SP1 and above. */
|
||||
return _xgetbv(idx);
|
||||
#else
|
||||
#ifndef NDEBUG
|
||||
printf("Unknown compiler. Cannot check xgetbv bits.\n");
|
||||
#endif
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@ -348,12 +348,12 @@ static unsigned char check_arm_cpu_feature(const char* feature)
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (filestream_gets(fp, line, sizeof(line)) != NULL)
|
||||
while (filestream_gets(fp, line, sizeof(line)))
|
||||
{
|
||||
if (strncmp(line, "Features\t: ", 11))
|
||||
continue;
|
||||
|
||||
if (strstr(line + 11, feature) != NULL)
|
||||
if (strstr(line + 11, feature))
|
||||
status = 1;
|
||||
|
||||
break;
|
||||
@ -424,8 +424,7 @@ static void cpulist_parse(CpuList* list, char **buf, ssize_t length)
|
||||
q = end;
|
||||
|
||||
/* Get first value */
|
||||
p = parse_decimal(p, q, &start_value);
|
||||
if (p == NULL)
|
||||
if (!(p = parse_decimal(p, q, &start_value)))
|
||||
return;
|
||||
|
||||
end_value = start_value;
|
||||
@ -435,8 +434,7 @@ static void cpulist_parse(CpuList* list, char **buf, ssize_t length)
|
||||
*/
|
||||
if (p < q && *p == '-')
|
||||
{
|
||||
p = parse_decimal(p+1, q, &end_value);
|
||||
if (p == NULL)
|
||||
if (!(p = parse_decimal(p+1, q, &end_value)))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -444,7 +442,7 @@ static void cpulist_parse(CpuList* list, char **buf, ssize_t length)
|
||||
for (val = start_value; val <= end_value; val++)
|
||||
{
|
||||
if ((unsigned)val < 32)
|
||||
list->mask |= (uint32_t)(1U << val);
|
||||
list->mask |= (uint32_t)(UINT32_C(1) << val);
|
||||
}
|
||||
|
||||
/* Jump to next item */
|
||||
@ -494,7 +492,7 @@ unsigned cpu_features_get_core_amount(void)
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
#elif defined(GEKKO)
|
||||
return 1;
|
||||
#elif defined(PSP) || defined(PS2)
|
||||
#elif defined(PSP) || defined(PS2) || defined(__CELLOS_LV2__)
|
||||
return 1;
|
||||
#elif defined(VITA)
|
||||
return 4;
|
||||
|
490
externals/libretro-common/file/archive_file.c
vendored
490
externals/libretro-common/file/archive_file.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (archive_file.c).
|
||||
@ -24,19 +24,6 @@
|
||||
#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>
|
||||
@ -45,110 +32,12 @@
|
||||
#include <lists/string_list.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
struct file_archive_file_data
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
int fd;
|
||||
#endif
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static size_t file_archive_size(file_archive_file_data_t *data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
return data->size;
|
||||
}
|
||||
|
||||
static const uint8_t *file_archive_data(file_archive_file_data_t *data)
|
||||
{
|
||||
if (!data)
|
||||
return NULL;
|
||||
return (const uint8_t*)data->data;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
/* Closes, unmaps and frees. */
|
||||
static void file_archive_free(file_archive_file_data_t *data)
|
||||
{
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (data->data)
|
||||
munmap(data->data, data->size);
|
||||
if (data->fd >= 0)
|
||||
close(data->fd);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static file_archive_file_data_t* file_archive_open(const char *path)
|
||||
{
|
||||
file_archive_file_data_t *data = (file_archive_file_data_t*)calloc(1, sizeof(*data));
|
||||
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
data->fd = open(path, O_RDONLY);
|
||||
|
||||
/* Failed to open archive. */
|
||||
if (data->fd < 0)
|
||||
goto error;
|
||||
|
||||
data->size = path_get_size(path);
|
||||
if (!data->size)
|
||||
return data;
|
||||
|
||||
data->data = mmap(NULL, data->size, PROT_READ, MAP_SHARED, data->fd, 0);
|
||||
if (data->data == MAP_FAILED)
|
||||
{
|
||||
data->data = NULL;
|
||||
|
||||
/* Failed to mmap() file */
|
||||
goto error;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
error:
|
||||
file_archive_free(data);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
|
||||
/* Closes, unmaps and frees. */
|
||||
static void file_archive_free(file_archive_file_data_t *data)
|
||||
{
|
||||
if (!data)
|
||||
return;
|
||||
if(data->data)
|
||||
free(data->data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static file_archive_file_data_t* file_archive_open(const char *path)
|
||||
{
|
||||
int64_t ret = -1;
|
||||
bool read_from_file = false;
|
||||
file_archive_file_data_t *data = (file_archive_file_data_t*)
|
||||
calloc(1, sizeof(*data));
|
||||
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
read_from_file = filestream_read_file(path, &data->data, &ret);
|
||||
|
||||
/* Failed to open archive? */
|
||||
if (!read_from_file || ret < 0)
|
||||
goto error;
|
||||
|
||||
data->size = ret;
|
||||
return data;
|
||||
|
||||
error:
|
||||
file_archive_free(data);
|
||||
return NULL;
|
||||
}
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
static int file_archive_get_file_list_cb(
|
||||
@ -169,37 +58,34 @@ static int file_archive_get_file_list_cb(
|
||||
size_t path_len = strlen(path);
|
||||
/* Checks if this entry is a directory or a file. */
|
||||
char last_char = path[path_len - 1];
|
||||
struct string_list *ext_list = NULL;
|
||||
struct string_list ext_list = {0};
|
||||
|
||||
/* Skip if directory. */
|
||||
if (last_char == '/' || last_char == '\\' )
|
||||
{
|
||||
string_list_free(ext_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ext_list = string_split(valid_exts, "|");
|
||||
|
||||
if (ext_list)
|
||||
string_list_initialize(&ext_list);
|
||||
if (string_split_noalloc(&ext_list, valid_exts, "|"))
|
||||
{
|
||||
const char *file_ext = path_get_extension(path);
|
||||
|
||||
if (!file_ext)
|
||||
{
|
||||
string_list_free(ext_list);
|
||||
string_list_deinitialize(&ext_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!string_list_find_elem_prefix(ext_list, ".", file_ext))
|
||||
if (!string_list_find_elem_prefix(&ext_list, ".", file_ext))
|
||||
{
|
||||
/* keep iterating */
|
||||
string_list_free(ext_list);
|
||||
string_list_deinitialize(&ext_list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE;
|
||||
string_list_free(ext_list);
|
||||
}
|
||||
|
||||
string_list_deinitialize(&ext_list);
|
||||
}
|
||||
|
||||
return string_list_append(userdata->list, path, attr);
|
||||
@ -216,11 +102,17 @@ static int file_archive_extract_cb(const char *name, const char *valid_exts,
|
||||
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;
|
||||
const char *delim;
|
||||
|
||||
new_path[0] = wanted_file[0] = '\0';
|
||||
delim = path_get_archive_delim(userdata->archive_path);
|
||||
|
||||
if (delim)
|
||||
{
|
||||
if (!string_is_equal_noncase(userdata->current_file_path, delim + 1))
|
||||
return 1; /* keep searching for the right file */
|
||||
}
|
||||
|
||||
new_path[0] = '\0';
|
||||
if (userdata->extraction_directory)
|
||||
fill_pathname_join(new_path, userdata->extraction_directory,
|
||||
path_basename(name), sizeof(new_path));
|
||||
@ -228,25 +120,14 @@ static int file_archive_extract_cb(const char *name, const char *valid_exts,
|
||||
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))
|
||||
checksum, userdata))
|
||||
{
|
||||
userdata->found_file = true;
|
||||
userdata->first_extracted_file_path = strdup(new_path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -273,14 +154,37 @@ static int file_archive_parse_file_init(file_archive_transfer_t *state,
|
||||
if (!state->backend)
|
||||
return -1;
|
||||
|
||||
state->handle = file_archive_open(path);
|
||||
if (!state->handle)
|
||||
state->archive_file = filestream_open(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
/* Failed to open archive. */
|
||||
if (!state->archive_file)
|
||||
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;
|
||||
state->archive_size = filestream_get_size(state->archive_file);
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
if (state->archive_size <= (256*1024*1024))
|
||||
{
|
||||
state->archive_mmap_fd = open(path, O_RDONLY);
|
||||
if (state->archive_mmap_fd)
|
||||
{
|
||||
state->archive_mmap_data = (uint8_t*)mmap(NULL, (size_t)state->archive_size,
|
||||
PROT_READ, MAP_SHARED, state->archive_mmap_fd, 0);
|
||||
|
||||
if (state->archive_mmap_data == (uint8_t*)MAP_FAILED)
|
||||
{
|
||||
close(state->archive_mmap_fd);
|
||||
state->archive_mmap_fd = 0;
|
||||
state->archive_mmap_data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
state->step_current = 0;
|
||||
state->step_total = 0;
|
||||
|
||||
return state->backend->archive_parse_file_init(state, path);
|
||||
}
|
||||
@ -288,35 +192,25 @@ static int file_archive_parse_file_init(file_archive_transfer_t *state,
|
||||
/**
|
||||
* 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.
|
||||
* Write data to file.
|
||||
*
|
||||
* Returns: true (1) on success, otherwise false (0).
|
||||
**/
|
||||
static int file_archive_decompress_data_to_file(
|
||||
file_archive_transfer_t *transfer,
|
||||
file_archive_file_handle_t *handle,
|
||||
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 (!handle)
|
||||
return 0;
|
||||
|
||||
#if 0
|
||||
handle->real_checksum = handle->backend->stream_crc_calculate(
|
||||
handle->real_checksum = transfer->backend->stream_crc_calculate(
|
||||
0, handle->data, size);
|
||||
if (handle->real_checksum != checksum)
|
||||
{
|
||||
@ -327,40 +221,14 @@ static int file_archive_decompress_data_to_file(
|
||||
#endif
|
||||
|
||||
if (!filestream_write_file(path, handle->data, size))
|
||||
{
|
||||
ret = false;
|
||||
goto end;
|
||||
}
|
||||
return 0;
|
||||
|
||||
end:
|
||||
|
||||
if (handle)
|
||||
{
|
||||
if (handle->backend)
|
||||
{
|
||||
if (handle->backend->stream_free)
|
||||
{
|
||||
#ifdef HAVE_7ZIP
|
||||
if (handle->backend != &sevenzip_backend)
|
||||
{
|
||||
handle->backend->stream_free(handle->stream);
|
||||
|
||||
if (handle->data)
|
||||
free(handle->data);
|
||||
}
|
||||
#else
|
||||
handle->backend->stream_free(handle->stream);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state)
|
||||
{
|
||||
if (!state || !state->handle)
|
||||
if (!state || !state->archive_file)
|
||||
return;
|
||||
|
||||
state->type = ARCHIVE_TRANSFER_DEINIT;
|
||||
@ -387,7 +255,7 @@ int file_archive_parse_file_iterate(
|
||||
{
|
||||
if (userdata)
|
||||
{
|
||||
userdata->context = state->stream;
|
||||
userdata->transfer = state;
|
||||
strlcpy(userdata->archive_path, file,
|
||||
sizeof(userdata->archive_path));
|
||||
}
|
||||
@ -397,14 +265,13 @@ int file_archive_parse_file_iterate(
|
||||
state->type = ARCHIVE_TRANSFER_DEINIT_ERROR;
|
||||
break;
|
||||
case ARCHIVE_TRANSFER_ITERATE:
|
||||
if (file_archive_get_file_backend(file))
|
||||
if (state->backend)
|
||||
{
|
||||
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);
|
||||
int ret = state->backend->archive_parse_file_iterate_step(
|
||||
state->context, valid_exts, userdata, file_cb);
|
||||
|
||||
if (ret == 1)
|
||||
state->step_current++; /* found another file */
|
||||
if (ret != 1)
|
||||
state->type = ARCHIVE_TRANSFER_DEINIT;
|
||||
if (ret == -1)
|
||||
@ -417,25 +284,31 @@ int file_archive_parse_file_iterate(
|
||||
case ARCHIVE_TRANSFER_DEINIT_ERROR:
|
||||
*returnerr = false;
|
||||
case ARCHIVE_TRANSFER_DEINIT:
|
||||
if (state->handle)
|
||||
if (state->context)
|
||||
{
|
||||
file_archive_free(state->handle);
|
||||
state->handle = NULL;
|
||||
if (state->backend->archive_parse_file_free)
|
||||
state->backend->archive_parse_file_free(state->context);
|
||||
state->context = NULL;
|
||||
}
|
||||
|
||||
if (state->stream && state->backend)
|
||||
if (state->archive_file)
|
||||
{
|
||||
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;
|
||||
filestream_close(state->archive_file);
|
||||
state->archive_file = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
if (state->archive_mmap_data)
|
||||
{
|
||||
munmap(state->archive_mmap_data, (size_t)state->archive_size);
|
||||
close(state->archive_mmap_fd);
|
||||
state->archive_mmap_fd = 0;
|
||||
state->archive_mmap_data = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (userdata)
|
||||
userdata->transfer = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -463,16 +336,19 @@ 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;
|
||||
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;
|
||||
state.type = ARCHIVE_TRANSFER_INIT;
|
||||
state.archive_file = NULL;
|
||||
#ifdef HAVE_MMAP
|
||||
state.archive_mmap_fd = 0;
|
||||
state.archive_mmap_data = NULL;
|
||||
#endif
|
||||
state.archive_size = 0;
|
||||
state.context = NULL;
|
||||
state.step_total = 0;
|
||||
state.step_current = 0;
|
||||
state.backend = NULL;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@ -486,17 +362,10 @@ static bool file_archive_walk(const char *file, const char *valid_exts,
|
||||
|
||||
int file_archive_parse_file_progress(file_archive_transfer_t *state)
|
||||
{
|
||||
ptrdiff_t delta = 0;
|
||||
|
||||
if (!state || state->archive_size == 0)
|
||||
if (!state || state->step_total == 0)
|
||||
return 0;
|
||||
|
||||
delta = state->directory - state->data;
|
||||
|
||||
if (!state->start_delta)
|
||||
state->start_delta = delta;
|
||||
|
||||
return (int)(((delta - state->start_delta) * 100) / (state->archive_size - state->start_delta));
|
||||
return (int)((state->step_current * 100) / (state->step_total));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -524,24 +393,18 @@ bool file_archive_extract_file(
|
||||
struct string_list *list = string_split(valid_exts, "|");
|
||||
|
||||
userdata.archive_path[0] = '\0';
|
||||
userdata.current_file_path[0] = '\0';
|
||||
userdata.first_extracted_file_path = NULL;
|
||||
userdata.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.transfer = NULL;
|
||||
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;
|
||||
@ -575,6 +438,37 @@ end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Warning: 'list' must zero initialised before
|
||||
* calling this function, otherwise memory leaks/
|
||||
* undefined behaviour will occur */
|
||||
bool file_archive_get_file_list_noalloc(struct string_list *list,
|
||||
const char *path,
|
||||
const char *valid_exts)
|
||||
{
|
||||
struct archive_extract_userdata userdata;
|
||||
|
||||
if (!list || !string_list_initialize(list))
|
||||
return false;
|
||||
|
||||
strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path));
|
||||
userdata.current_file_path[0] = '\0';
|
||||
userdata.first_extracted_file_path = NULL;
|
||||
userdata.extraction_directory = NULL;
|
||||
userdata.archive_path_size = 0;
|
||||
userdata.ext = NULL;
|
||||
userdata.list = list;
|
||||
userdata.found_file = false;
|
||||
userdata.list_only = true;
|
||||
userdata.crc = 0;
|
||||
userdata.transfer = NULL;
|
||||
userdata.dec = NULL;
|
||||
|
||||
if (!file_archive_walk(path, valid_exts,
|
||||
file_archive_get_file_list_cb, &userdata))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* file_archive_get_file_list:
|
||||
* @path : filename path of archive
|
||||
@ -587,82 +481,56 @@ struct string_list *file_archive_get_file_list(const char *path,
|
||||
struct archive_extract_userdata userdata;
|
||||
|
||||
strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path));
|
||||
userdata.current_file_path[0] = '\0';
|
||||
userdata.first_extracted_file_path = NULL;
|
||||
userdata.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.transfer = NULL;
|
||||
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;
|
||||
|
||||
return NULL;
|
||||
if (!file_archive_walk(path, valid_exts,
|
||||
file_archive_get_file_list_cb, &userdata))
|
||||
goto error;
|
||||
|
||||
return userdata.list;
|
||||
|
||||
error:
|
||||
if (userdata.list)
|
||||
{
|
||||
string_list_free(userdata.list);
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
return userdata.list;
|
||||
}
|
||||
|
||||
bool file_archive_perform_mode(const char *path, const char *valid_exts,
|
||||
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
|
||||
uint32_t crc32, struct archive_extract_userdata *userdata)
|
||||
{
|
||||
switch (cmode)
|
||||
file_archive_file_handle_t handle;
|
||||
int ret;
|
||||
|
||||
if (!userdata->transfer || !userdata->transfer->backend)
|
||||
return false;
|
||||
|
||||
handle.data = NULL;
|
||||
handle.real_checksum = 0;
|
||||
|
||||
if (!userdata->transfer->backend->stream_decompress_data_to_file_init(
|
||||
userdata->transfer->context, &handle, cdata, cmode, csize, size))
|
||||
return false;
|
||||
|
||||
do
|
||||
{
|
||||
case ARCHIVE_MODE_UNCOMPRESSED:
|
||||
if (!filestream_write_file(path, cdata, size))
|
||||
return false;
|
||||
break;
|
||||
ret = userdata->transfer->backend->stream_decompress_data_to_file_iterate(
|
||||
userdata->transfer->context, &handle);
|
||||
}while (ret == 0);
|
||||
|
||||
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)
|
||||
return false;
|
||||
|
||||
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 (!file_archive_decompress_data_to_file(&handle,
|
||||
ret, path, valid_exts,
|
||||
cdata, csize, size, crc32))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if (ret == -1 || !file_archive_decompress_data_to_file(
|
||||
userdata->transfer, &handle, path,
|
||||
size, crc32))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -823,7 +691,7 @@ const struct file_archive_file_backend* file_archive_get_file_backend(const char
|
||||
uint32_t file_archive_get_file_crc32(const char *path)
|
||||
{
|
||||
file_archive_transfer_t state;
|
||||
struct archive_extract_userdata userdata = {{0}};
|
||||
struct archive_extract_userdata userdata = {0};
|
||||
bool returnerr = false;
|
||||
const char *archive_path = NULL;
|
||||
bool contains_compressed = path_contains_compressed_file(path);
|
||||
@ -837,14 +705,17 @@ uint32_t file_archive_get_file_crc32(const char *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;
|
||||
state.type = ARCHIVE_TRANSFER_INIT;
|
||||
state.archive_file = NULL;
|
||||
#ifdef HAVE_MMAP
|
||||
state.archive_mmap_fd = 0;
|
||||
state.archive_mmap_data = NULL;
|
||||
#endif
|
||||
state.archive_size = 0;
|
||||
state.context = NULL;
|
||||
state.step_total = 0;
|
||||
state.step_current = 0;
|
||||
state.backend = NULL;
|
||||
|
||||
/* Initialize and open archive first.
|
||||
Sets next state type to ITERATE. */
|
||||
@ -869,7 +740,7 @@ uint32_t file_archive_get_file_crc32(const char *path)
|
||||
/* Stop when the right file in the archive is found. */
|
||||
if (archive_path)
|
||||
{
|
||||
if (string_is_equal(userdata.extracted_file_path, archive_path))
|
||||
if (string_is_equal(userdata.current_file_path, archive_path))
|
||||
break;
|
||||
}
|
||||
else
|
||||
@ -878,8 +749,5 @@ uint32_t file_archive_get_file_crc32(const char *path)
|
||||
|
||||
file_archive_parse_file_iterate_stop(&state);
|
||||
|
||||
if (userdata.crc)
|
||||
return userdata.crc;
|
||||
|
||||
return 0;
|
||||
return userdata.crc;
|
||||
}
|
||||
|
107
externals/libretro-common/file/archive_file_7z.c
vendored
107
externals/libretro-common/file/archive_file_7z.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (archive_file_sevenzip.c).
|
||||
@ -46,7 +46,9 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct sevenzip_context_t {
|
||||
struct sevenzip_context_t
|
||||
{
|
||||
uint8_t *output;
|
||||
CFileInStream archiveStream;
|
||||
CLookToRead lookStream;
|
||||
ISzAlloc allocImp;
|
||||
@ -54,10 +56,9 @@ struct sevenzip_context_t {
|
||||
CSzArEx db;
|
||||
size_t temp_size;
|
||||
uint32_t block_index;
|
||||
uint32_t index;
|
||||
uint32_t parse_index;
|
||||
uint32_t decompress_index;
|
||||
uint32_t packIndex;
|
||||
uint8_t *output;
|
||||
file_archive_file_handle_t *handle;
|
||||
};
|
||||
|
||||
static void *sevenzip_stream_alloc_impl(void *p, size_t size)
|
||||
@ -96,14 +97,13 @@ static void* sevenzip_stream_new(void)
|
||||
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)
|
||||
static void sevenzip_parse_file_free(void *context)
|
||||
{
|
||||
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)data;
|
||||
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)context;
|
||||
|
||||
if (!sevenzip_context)
|
||||
return;
|
||||
@ -112,11 +112,12 @@ static void sevenzip_stream_free(void *data)
|
||||
{
|
||||
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);
|
||||
|
||||
free(sevenzip_context);
|
||||
}
|
||||
|
||||
/* Extract the relative path (needle) from a 7z archive
|
||||
@ -124,7 +125,7 @@ static void sevenzip_stream_free(void *data)
|
||||
* If optional_outfile is set, extract to that instead
|
||||
* and don't allocate buffer.
|
||||
*/
|
||||
static int sevenzip_file_read(
|
||||
static int64_t sevenzip_file_read(
|
||||
const char *path,
|
||||
const char *needle, void **buf,
|
||||
const char *optional_outfile)
|
||||
@ -135,7 +136,7 @@ static int sevenzip_file_read(
|
||||
ISzAlloc allocTempImp;
|
||||
CSzArEx db;
|
||||
uint8_t *output = 0;
|
||||
long outsize = -1;
|
||||
int64_t outsize = -1;
|
||||
|
||||
/*These are the allocation routines.
|
||||
* Currently using the non-standard 7zip choices. */
|
||||
@ -254,9 +255,9 @@ static int sevenzip_file_read(
|
||||
if (res != SZ_OK)
|
||||
break; /* This goes to the error section. */
|
||||
|
||||
outsize = outSizeProcessed;
|
||||
outsize = (int64_t)outSizeProcessed;
|
||||
|
||||
if (optional_outfile != NULL)
|
||||
if (optional_outfile)
|
||||
{
|
||||
const void *ptr = (const void*)(output + offset);
|
||||
|
||||
@ -274,7 +275,7 @@ static int sevenzip_file_read(
|
||||
* 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);
|
||||
*buf = malloc((size_t)(outsize + 1));
|
||||
((char*)(*buf))[outsize] = '\0';
|
||||
memcpy(*buf,output + offset,outsize);
|
||||
}
|
||||
@ -300,28 +301,29 @@ static int sevenzip_file_read(
|
||||
SzArEx_Free(&db, &allocImp);
|
||||
File_Close(&archiveStream.file);
|
||||
|
||||
return (int)outsize;
|
||||
return 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)
|
||||
void *context, file_archive_file_handle_t *handle,
|
||||
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
|
||||
{
|
||||
struct sevenzip_context_t *sevenzip_context =
|
||||
(struct sevenzip_context_t*)handle->stream;
|
||||
(struct sevenzip_context_t*)context;
|
||||
|
||||
if (!sevenzip_context)
|
||||
return false;
|
||||
|
||||
sevenzip_context->handle = handle;
|
||||
sevenzip_context->decompress_index = (uint32_t)(size_t)cdata;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
|
||||
static int sevenzip_stream_decompress_data_to_file_iterate(
|
||||
void *context, file_archive_file_handle_t *handle)
|
||||
{
|
||||
struct sevenzip_context_t *sevenzip_context =
|
||||
(struct sevenzip_context_t*)data;
|
||||
(struct sevenzip_context_t*)context;
|
||||
|
||||
SRes res = SZ_ERROR_FAIL;
|
||||
size_t output_size = 0;
|
||||
@ -329,7 +331,7 @@ static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
|
||||
size_t outSizeProcessed = 0;
|
||||
|
||||
res = SzArEx_Extract(&sevenzip_context->db,
|
||||
&sevenzip_context->lookStream.s, sevenzip_context->index,
|
||||
&sevenzip_context->lookStream.s, sevenzip_context->decompress_index,
|
||||
&sevenzip_context->block_index, &sevenzip_context->output,
|
||||
&output_size, &offset, &outSizeProcessed,
|
||||
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp);
|
||||
@ -337,8 +339,8 @@ static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
|
||||
if (res != SZ_OK)
|
||||
return 0;
|
||||
|
||||
if (sevenzip_context->handle)
|
||||
sevenzip_context->handle->data = sevenzip_context->output + offset;
|
||||
if (handle)
|
||||
handle->data = sevenzip_context->output + offset;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -346,16 +348,21 @@ static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
|
||||
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();
|
||||
uint8_t magic_buf[SEVENZIP_MAGIC_LEN];
|
||||
struct sevenzip_context_t *sevenzip_context = NULL;
|
||||
|
||||
if (state->archive_size < SEVENZIP_MAGIC_LEN)
|
||||
goto error;
|
||||
|
||||
if (string_is_not_equal_fast(state->data, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN))
|
||||
filestream_seek(state->archive_file, 0, SEEK_SET);
|
||||
if (filestream_read(state->archive_file, magic_buf, SEVENZIP_MAGIC_LEN) != SEVENZIP_MAGIC_LEN)
|
||||
goto error;
|
||||
|
||||
state->stream = sevenzip_context;
|
||||
if (string_is_not_equal_fast(magic_buf, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN))
|
||||
goto error;
|
||||
|
||||
sevenzip_context = (struct sevenzip_context_t*)sevenzip_stream_new();
|
||||
state->context = sevenzip_context;
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32)
|
||||
if (!string_is_empty(file))
|
||||
@ -391,27 +398,28 @@ static int sevenzip_parse_file_init(file_archive_transfer_t *state,
|
||||
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp) != SZ_OK)
|
||||
goto error;
|
||||
|
||||
state->step_total = sevenzip_context->db.db.NumFiles;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (sevenzip_context)
|
||||
sevenzip_stream_free(sevenzip_context);
|
||||
sevenzip_parse_file_free(sevenzip_context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sevenzip_parse_file_iterate_step_internal(
|
||||
file_archive_transfer_t *state, char *filename,
|
||||
struct sevenzip_context_t *sevenzip_context, char *filename,
|
||||
const uint8_t **cdata, unsigned *cmode,
|
||||
uint32_t *size, uint32_t *csize, uint32_t *checksum,
|
||||
unsigned *payback, struct archive_extract_userdata *userdata)
|
||||
{
|
||||
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)state->stream;
|
||||
const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->index;
|
||||
const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->parse_index;
|
||||
|
||||
if (sevenzip_context->index < sevenzip_context->db.db.NumFiles)
|
||||
if (sevenzip_context->parse_index < sevenzip_context->db.db.NumFiles)
|
||||
{
|
||||
size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db,
|
||||
sevenzip_context->index, NULL);
|
||||
sevenzip_context->parse_index, NULL);
|
||||
uint64_t compressed_size = 0;
|
||||
|
||||
if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams)
|
||||
@ -431,7 +439,7 @@ static int sevenzip_parse_file_iterate_step_internal(
|
||||
|
||||
infile[0] = '\0';
|
||||
|
||||
SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index,
|
||||
SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->parse_index,
|
||||
temp);
|
||||
|
||||
if (temp)
|
||||
@ -446,10 +454,12 @@ static int sevenzip_parse_file_iterate_step_internal(
|
||||
|
||||
strlcpy(filename, infile, PATH_MAX_LENGTH);
|
||||
|
||||
*cmode = ARCHIVE_MODE_COMPRESSED;
|
||||
*cmode = 0; /* unused for 7zip */
|
||||
*checksum = file->Crc;
|
||||
*size = (uint32_t)file->Size;
|
||||
*csize = (uint32_t)compressed_size;
|
||||
|
||||
*cdata = (uint8_t *)(size_t)sevenzip_context->parse_index;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -460,39 +470,37 @@ static int sevenzip_parse_file_iterate_step_internal(
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state,
|
||||
static int sevenzip_parse_file_iterate_step(void *context,
|
||||
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;
|
||||
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)context;
|
||||
int ret;
|
||||
|
||||
filename[0] = '\0';
|
||||
userdata->current_file_path[0] = '\0';
|
||||
|
||||
ret = sevenzip_parse_file_iterate_step_internal(state, filename,
|
||||
ret = sevenzip_parse_file_iterate_step_internal(sevenzip_context,
|
||||
userdata->current_file_path,
|
||||
&cdata, &cmode, &size, &csize,
|
||||
&checksum, &payload, userdata);
|
||||
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
|
||||
userdata->extracted_file_path = filename;
|
||||
userdata->crc = checksum;
|
||||
|
||||
if (file_cb && !file_cb(filename, valid_exts, cdata, cmode,
|
||||
if (file_cb && !file_cb(userdata->current_file_path, valid_exts,
|
||||
cdata, cmode,
|
||||
csize, size, checksum, userdata))
|
||||
return 0;
|
||||
|
||||
sevenzip_context = (struct sevenzip_context_t*)state->stream;
|
||||
|
||||
sevenzip_context->index += payload;
|
||||
sevenzip_context->parse_index += payload;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -504,13 +512,12 @@ static uint32_t sevenzip_stream_crc32_calculate(uint32_t crc,
|
||||
}
|
||||
|
||||
const struct file_archive_file_backend sevenzip_backend = {
|
||||
sevenzip_stream_new,
|
||||
sevenzip_stream_free,
|
||||
sevenzip_parse_file_init,
|
||||
sevenzip_parse_file_iterate_step,
|
||||
sevenzip_parse_file_free,
|
||||
sevenzip_stream_decompress_data_to_file_init,
|
||||
sevenzip_stream_decompress_data_to_file_iterate,
|
||||
sevenzip_stream_crc32_calculate,
|
||||
sevenzip_file_read,
|
||||
sevenzip_parse_file_init,
|
||||
sevenzip_parse_file_iterate_step,
|
||||
"7z"
|
||||
};
|
||||
|
448
externals/libretro-common/file/archive_file_zlib.c
vendored
448
externals/libretro-common/file/archive_file_zlib.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (archive_file_zlib.c).
|
||||
@ -41,6 +41,23 @@
|
||||
#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
|
||||
#endif
|
||||
|
||||
enum file_archive_compression_mode
|
||||
{
|
||||
ZIP_MODE_STORED = 0,
|
||||
ZIP_MODE_DEFLATED = 8
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct file_archive_transfer *state;
|
||||
uint8_t *directory;
|
||||
uint8_t *directory_entry;
|
||||
uint8_t *directory_end;
|
||||
void *current_stream;
|
||||
uint8_t *compressed_data;
|
||||
uint8_t *decompressed_data;
|
||||
} zip_context_t;
|
||||
|
||||
static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
|
||||
{
|
||||
unsigned i;
|
||||
@ -53,69 +70,147 @@ static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
|
||||
return val;
|
||||
}
|
||||
|
||||
static void *zlib_stream_new(void)
|
||||
static void zip_context_free_stream(
|
||||
zip_context_t *zip_context, bool keep_decompressed)
|
||||
{
|
||||
return zlib_inflate_backend.stream_new();
|
||||
}
|
||||
|
||||
static void zlib_stream_free(void *stream)
|
||||
{
|
||||
zlib_inflate_backend.stream_free(stream);
|
||||
if (zip_context->current_stream)
|
||||
{
|
||||
zlib_inflate_backend.stream_free(zip_context->current_stream);
|
||||
zip_context->current_stream = NULL;
|
||||
}
|
||||
if (zip_context->compressed_data)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
if (!zip_context->state->archive_mmap_data)
|
||||
#endif
|
||||
{
|
||||
free(zip_context->compressed_data);
|
||||
zip_context->compressed_data = NULL;
|
||||
}
|
||||
}
|
||||
if (zip_context->decompressed_data && !keep_decompressed)
|
||||
{
|
||||
free(zip_context->decompressed_data);
|
||||
zip_context->decompressed_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool zlib_stream_decompress_data_to_file_init(
|
||||
file_archive_file_handle_t *handle,
|
||||
const uint8_t *cdata, uint32_t csize, uint32_t size)
|
||||
void *context, file_archive_file_handle_t *handle,
|
||||
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
|
||||
{
|
||||
if (!handle)
|
||||
return false;
|
||||
zip_context_t *zip_context = (zip_context_t *)context;
|
||||
struct file_archive_transfer *state = zip_context->state;
|
||||
uint8_t local_header_buf[4];
|
||||
uint8_t *local_header;
|
||||
uint32_t offsetNL, offsetEL;
|
||||
int64_t offsetData;
|
||||
|
||||
handle->stream = zlib_inflate_backend.stream_new();
|
||||
/* free previous data and stream if left unfinished */
|
||||
zip_context_free_stream(zip_context, false);
|
||||
|
||||
if (!handle->stream)
|
||||
goto error;
|
||||
/* seek past most of the local directory header */
|
||||
#ifdef HAVE_MMAP
|
||||
if (state->archive_mmap_data)
|
||||
{
|
||||
local_header = state->archive_mmap_data + (size_t)cdata + 26;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
filestream_seek(state->archive_file, (int64_t)(size_t)cdata + 26, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(state->archive_file, local_header_buf, 4) != 4)
|
||||
goto error;
|
||||
local_header = local_header_buf;
|
||||
}
|
||||
|
||||
if (zlib_inflate_backend.define)
|
||||
zlib_inflate_backend.define(handle->stream, "window_bits", (uint32_t)-MAX_WBITS);
|
||||
offsetNL = read_le(local_header, 2); /* file name length */
|
||||
offsetEL = read_le(local_header + 2, 2); /* extra field length */
|
||||
offsetData = (int64_t)(size_t)cdata + 26 + 4 + offsetNL + offsetEL;
|
||||
|
||||
handle->data = (uint8_t*)malloc(size);
|
||||
#ifdef HAVE_MMAP
|
||||
if (state->archive_mmap_data)
|
||||
{
|
||||
zip_context->compressed_data = state->archive_mmap_data + (size_t)offsetData;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* allocate memory for the compressed data */
|
||||
zip_context->compressed_data = (uint8_t*)malloc(csize);
|
||||
if (!zip_context->compressed_data)
|
||||
goto error;
|
||||
|
||||
if (!handle->data)
|
||||
goto error;
|
||||
/* skip over name and extra data */
|
||||
filestream_seek(state->archive_file, offsetData, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(state->archive_file, zip_context->compressed_data, csize) != csize)
|
||||
goto error;
|
||||
}
|
||||
|
||||
zlib_inflate_backend.set_in(handle->stream,
|
||||
(const uint8_t*)cdata, csize);
|
||||
zlib_inflate_backend.set_out(handle->stream,
|
||||
handle->data, size);
|
||||
switch (cmode)
|
||||
{
|
||||
case ZIP_MODE_STORED:
|
||||
handle->data = zip_context->compressed_data;
|
||||
return true;
|
||||
|
||||
return true;
|
||||
case ZIP_MODE_DEFLATED:
|
||||
zip_context->current_stream = zlib_inflate_backend.stream_new();
|
||||
if (!zip_context->current_stream)
|
||||
goto error;
|
||||
|
||||
if (zlib_inflate_backend.define)
|
||||
zlib_inflate_backend.define(zip_context->current_stream, "window_bits", (uint32_t)-MAX_WBITS);
|
||||
|
||||
zip_context->decompressed_data = (uint8_t*)malloc(size);
|
||||
|
||||
if (!zip_context->decompressed_data)
|
||||
goto error;
|
||||
|
||||
zlib_inflate_backend.set_in(zip_context->current_stream,
|
||||
zip_context->compressed_data, csize);
|
||||
zlib_inflate_backend.set_out(zip_context->current_stream,
|
||||
zip_context->decompressed_data, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
error:
|
||||
if (handle->stream)
|
||||
zlib_inflate_backend.stream_free(handle->stream);
|
||||
if (handle->data)
|
||||
free(handle->data);
|
||||
|
||||
zip_context_free_stream(zip_context, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int zlib_stream_decompress_data_to_file_iterate(void *stream)
|
||||
static int zlib_stream_decompress_data_to_file_iterate(
|
||||
void *context, file_archive_file_handle_t *handle)
|
||||
{
|
||||
zip_context_t *zip_context = (zip_context_t *)context;
|
||||
bool zstatus;
|
||||
uint32_t rd, wn;
|
||||
enum trans_stream_error terror;
|
||||
|
||||
if (!stream)
|
||||
return -1;
|
||||
if (!zip_context->current_stream)
|
||||
{
|
||||
/* file was uncompressed or decompression finished before */
|
||||
return 1;
|
||||
}
|
||||
|
||||
zstatus = zlib_inflate_backend.trans(stream, false, &rd, &wn, &terror);
|
||||
|
||||
if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
|
||||
return -1;
|
||||
zstatus = zlib_inflate_backend.trans(zip_context->current_stream, false, &rd, &wn, &terror);
|
||||
|
||||
if (zstatus && !terror)
|
||||
{
|
||||
/* successfully decompressed entire file */
|
||||
zip_context_free_stream(zip_context, true);
|
||||
handle->data = zip_context->decompressed_data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
|
||||
{
|
||||
/* error during stream processing */
|
||||
zip_context_free_stream(zip_context, false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* still more data to process */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -126,49 +221,51 @@ static uint32_t zlib_stream_crc32_calculate(uint32_t crc,
|
||||
}
|
||||
|
||||
static bool zip_file_decompressed_handle(
|
||||
file_archive_file_handle_t *handle,
|
||||
const uint8_t *cdata, uint32_t csize,
|
||||
file_archive_transfer_t *transfer,
|
||||
file_archive_file_handle_t* handle,
|
||||
const uint8_t *cdata, unsigned cmode, uint32_t csize,
|
||||
uint32_t size, uint32_t crc32)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
handle->backend = &zlib_backend;
|
||||
transfer->backend = &zlib_backend;
|
||||
|
||||
if (!handle->backend->stream_decompress_data_to_file_init(
|
||||
handle, cdata, csize, size))
|
||||
if (!transfer->backend->stream_decompress_data_to_file_init(
|
||||
transfer->context, handle, cdata, cmode, csize, size))
|
||||
return false;
|
||||
|
||||
do
|
||||
{
|
||||
ret = handle->backend->stream_decompress_data_to_file_iterate(
|
||||
handle->stream);
|
||||
}while(ret == 0);
|
||||
ret = transfer->backend->stream_decompress_data_to_file_iterate(
|
||||
transfer->context, handle);
|
||||
}while (ret == 0);
|
||||
|
||||
#if 0
|
||||
handle->real_checksum = handle->backend->stream_crc_calculate(0,
|
||||
handle->real_checksum = transfer->backend->stream_crc_calculate(0,
|
||||
handle->data, size);
|
||||
|
||||
if (handle->real_checksum != crc32)
|
||||
goto error;
|
||||
#endif
|
||||
{
|
||||
if (handle->data)
|
||||
free(handle->data);
|
||||
|
||||
if (handle->stream)
|
||||
free(handle->stream);
|
||||
handle->data = NULL;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *opt_file;
|
||||
char *needle;
|
||||
void **buf;
|
||||
size_t size;
|
||||
bool found;
|
||||
} decomp_state_t;
|
||||
|
||||
/* Extract the relative path (needle) from a
|
||||
* ZIP archive (path) and allocate a buffer for it to write it in.
|
||||
*
|
||||
@ -182,212 +279,251 @@ static int zip_file_decompressed(
|
||||
uint32_t csize, uint32_t size,
|
||||
uint32_t crc32, struct archive_extract_userdata *userdata)
|
||||
{
|
||||
decomp_state_t* decomp_state = (decomp_state_t*)userdata->cb_data;
|
||||
char last_char = name[strlen(name) - 1];
|
||||
/* Ignore directories. */
|
||||
if (last_char == '/' || last_char == '\\')
|
||||
return 1;
|
||||
|
||||
if (strstr(name, userdata->decomp_state.needle))
|
||||
if (strstr(name, 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 (zip_file_decompressed_handle(userdata->transfer,
|
||||
&handle, cdata, cmode, csize, size, crc32))
|
||||
{
|
||||
if (userdata->decomp_state.opt_file != 0)
|
||||
if (decomp_state->opt_file != 0)
|
||||
{
|
||||
/* Called in case core has need_fullpath enabled. */
|
||||
char *buf = (char*)malloc(size);
|
||||
bool success = filestream_write_file(decomp_state->opt_file, handle.data, size);
|
||||
|
||||
if (buf)
|
||||
{
|
||||
memcpy(buf, handle.data, size);
|
||||
free(handle.data);
|
||||
handle.data = NULL;
|
||||
|
||||
if (!filestream_write_file(userdata->decomp_state.opt_file, buf, size))
|
||||
goto_error = true;
|
||||
}
|
||||
decomp_state->size = 0;
|
||||
|
||||
free(buf);
|
||||
|
||||
userdata->decomp_state.size = 0;
|
||||
if (!success)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Called in case core has need_fullpath disabled.
|
||||
* Will copy decompressed content directly into
|
||||
* Will move decompressed content directly into
|
||||
* RetroArch's ROM buffer. */
|
||||
*userdata->decomp_state.buf = malloc(size);
|
||||
memcpy(*userdata->decomp_state.buf, handle.data, size);
|
||||
*decomp_state->buf = handle.data;
|
||||
handle.data = NULL;
|
||||
|
||||
userdata->decomp_state.size = size;
|
||||
decomp_state->size = size;
|
||||
}
|
||||
}
|
||||
|
||||
if (handle.data)
|
||||
free(handle.data);
|
||||
|
||||
if (goto_error)
|
||||
return 0;
|
||||
decomp_state->found = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int zip_file_read(
|
||||
static int64_t 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}};
|
||||
file_archive_transfer_t state = {ARCHIVE_TRANSFER_INIT};
|
||||
decomp_state_t decomp = {0};
|
||||
struct archive_extract_userdata userdata = {0};
|
||||
bool returnerr = true;
|
||||
int ret = 0;
|
||||
|
||||
zlib.type = ARCHIVE_TRANSFER_INIT;
|
||||
zlib.archive_size = 0;
|
||||
zlib.start_delta = 0;
|
||||
zlib.handle = NULL;
|
||||
zlib.stream = NULL;
|
||||
zlib.footer = NULL;
|
||||
zlib.directory = NULL;
|
||||
zlib.data = NULL;
|
||||
zlib.backend = NULL;
|
||||
|
||||
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);
|
||||
decomp.needle = strdup(needle);
|
||||
if (optional_outfile)
|
||||
userdata.decomp_state.opt_file = strdup(optional_outfile);
|
||||
decomp.opt_file = strdup(optional_outfile);
|
||||
|
||||
userdata.transfer = &state;
|
||||
userdata.cb_data = &decomp;
|
||||
decomp.buf = buf;
|
||||
|
||||
do
|
||||
{
|
||||
ret = file_archive_parse_file_iterate(&zlib, &returnerr, path,
|
||||
ret = file_archive_parse_file_iterate(&state, &returnerr, path,
|
||||
"", zip_file_decompressed, &userdata);
|
||||
if (!returnerr)
|
||||
break;
|
||||
}while(ret == 0 && !userdata.decomp_state.found);
|
||||
}while (ret == 0 && !decomp.found);
|
||||
|
||||
file_archive_parse_file_iterate_stop(&zlib);
|
||||
file_archive_parse_file_iterate_stop(&state);
|
||||
|
||||
if (userdata.decomp_state.opt_file)
|
||||
free(userdata.decomp_state.opt_file);
|
||||
if (userdata.decomp_state.needle)
|
||||
free(userdata.decomp_state.needle);
|
||||
if (decomp.opt_file)
|
||||
free(decomp.opt_file);
|
||||
if (decomp.needle)
|
||||
free(decomp.needle);
|
||||
|
||||
if (!userdata.decomp_state.found)
|
||||
if (!decomp.found)
|
||||
return -1;
|
||||
|
||||
return (int)userdata.decomp_state.size;
|
||||
return (int64_t)decomp.size;
|
||||
}
|
||||
|
||||
static int zip_parse_file_init(file_archive_transfer_t *state,
|
||||
const char *file)
|
||||
{
|
||||
if (state->archive_size < 22)
|
||||
uint8_t footer_buf[1024];
|
||||
uint8_t *footer = footer_buf;
|
||||
int64_t read_pos = state->archive_size;
|
||||
int64_t read_block = MIN(read_pos, sizeof(footer_buf));
|
||||
int64_t directory_size, directory_offset;
|
||||
zip_context_t *zip_context = NULL;
|
||||
|
||||
/* Minimal ZIP file size is 22 bytes */
|
||||
if (read_block < 22)
|
||||
return -1;
|
||||
|
||||
state->footer = state->data + state->archive_size - 22;
|
||||
|
||||
for (;; state->footer--)
|
||||
/* Find the end of central directory record by scanning
|
||||
* the file from the end towards the beginning.
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
if (state->footer <= state->data + 22)
|
||||
return -1;
|
||||
if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
|
||||
if (--footer < footer_buf)
|
||||
{
|
||||
unsigned comment_len = read_le(state->footer + 20, 2);
|
||||
if (state->footer + 22 + comment_len == state->data + state->archive_size)
|
||||
break;
|
||||
if (read_pos <= 0)
|
||||
return -1; /* reached beginning of file */
|
||||
|
||||
/* Read 21 bytes of overlaps except on the first block. */
|
||||
if (read_pos == state->archive_size)
|
||||
read_pos = read_pos - read_block;
|
||||
else
|
||||
read_pos = MAX(read_pos - read_block + 21, 0);
|
||||
|
||||
/* Seek to read_pos and read read_block bytes. */
|
||||
filestream_seek(state->archive_file, read_pos, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(state->archive_file, footer_buf, read_block) != read_block)
|
||||
return -1;
|
||||
|
||||
footer = footer_buf + read_block - 22;
|
||||
}
|
||||
if (read_le(footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
|
||||
{
|
||||
unsigned comment_len = read_le(footer + 20, 2);
|
||||
if (read_pos + (footer - footer_buf) + 22 + comment_len == state->archive_size)
|
||||
break; /* found it! */
|
||||
}
|
||||
}
|
||||
|
||||
state->directory = state->data + read_le(state->footer + 16, 4);
|
||||
/* Read directory info and do basic sanity checks. */
|
||||
directory_size = read_le(footer + 12, 4);
|
||||
directory_offset = read_le(footer + 16, 4);
|
||||
if (directory_size > state->archive_size
|
||||
|| directory_offset > state->archive_size)
|
||||
return -1;
|
||||
|
||||
/* This is a ZIP file, allocate one block of memory for both the
|
||||
* context and the entire directory, then read the directory.
|
||||
*/
|
||||
zip_context = (zip_context_t*)malloc(sizeof(zip_context_t) + (size_t)directory_size);
|
||||
zip_context->state = state;
|
||||
zip_context->directory = (uint8_t*)(zip_context + 1);
|
||||
zip_context->directory_entry = zip_context->directory;
|
||||
zip_context->directory_end = zip_context->directory + (size_t)directory_size;
|
||||
zip_context->current_stream = NULL;
|
||||
zip_context->compressed_data = NULL;
|
||||
zip_context->decompressed_data = NULL;
|
||||
|
||||
filestream_seek(state->archive_file, directory_offset, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(state->archive_file, zip_context->directory, directory_size) != directory_size)
|
||||
{
|
||||
free(zip_context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->context = zip_context;
|
||||
state->step_total = read_le(footer + 10, 2); /* total entries */;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zip_parse_file_iterate_step_internal(
|
||||
file_archive_transfer_t *state, char *filename,
|
||||
zip_context_t * zip_context, 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);
|
||||
uint8_t *entry = zip_context->directory_entry;
|
||||
uint32_t signature, namelength, extralength, commentlength, offset;
|
||||
|
||||
if (entry < zip_context->directory || entry >= zip_context->directory_end)
|
||||
return 0;
|
||||
|
||||
signature = read_le(zip_context->directory_entry + 0, 4);
|
||||
|
||||
if (signature != CENTRAL_FILE_HEADER_SIGNATURE)
|
||||
return 0;
|
||||
|
||||
*cmode = read_le(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 */
|
||||
*cmode = read_le(zip_context->directory_entry + 10, 2); /* compression mode, 0 = store, 8 = deflate */
|
||||
*checksum = read_le(zip_context->directory_entry + 16, 4); /* CRC32 */
|
||||
*csize = read_le(zip_context->directory_entry + 20, 4); /* compressed size */
|
||||
*size = read_le(zip_context->directory_entry + 24, 4); /* uncompressed size */
|
||||
|
||||
namelength = read_le(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 */
|
||||
namelength = read_le(zip_context->directory_entry + 28, 2); /* file name length */
|
||||
extralength = read_le(zip_context->directory_entry + 30, 2); /* extra field length */
|
||||
commentlength = read_le(zip_context->directory_entry + 32, 2); /* file comment length */
|
||||
|
||||
if (namelength >= PATH_MAX_LENGTH)
|
||||
return -1;
|
||||
|
||||
memcpy(filename, state->directory + 46, namelength); /* file name */
|
||||
memcpy(filename, zip_context->directory_entry + 46, namelength); /* file name */
|
||||
filename[namelength] = '\0';
|
||||
|
||||
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 */
|
||||
offset = read_le(zip_context->directory_entry + 42, 4); /* relative offset of local file header */
|
||||
|
||||
*cdata = state->data + offset + 30 + offsetNL + offsetEL;
|
||||
*cdata = (uint8_t*)(size_t)offset; /* store file offset in data pointer */
|
||||
|
||||
*payback = 46 + namelength + extralength + commentlength;
|
||||
*payback = 46 + namelength + extralength + commentlength;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int zip_parse_file_iterate_step(file_archive_transfer_t *state,
|
||||
static int zip_parse_file_iterate_step(void *context,
|
||||
const char *valid_exts, struct archive_extract_userdata *userdata,
|
||||
file_archive_file_cb file_cb)
|
||||
{
|
||||
char filename[PATH_MAX_LENGTH] = {0};
|
||||
zip_context_t *zip_context = (zip_context_t *)context;
|
||||
const uint8_t *cdata = NULL;
|
||||
uint32_t checksum = 0;
|
||||
uint32_t size = 0;
|
||||
uint32_t csize = 0;
|
||||
unsigned cmode = 0;
|
||||
unsigned payload = 0;
|
||||
int ret = zip_parse_file_iterate_step_internal(
|
||||
state, filename, &cdata, &cmode, &size, &csize, &checksum, &payload);
|
||||
int ret = zip_parse_file_iterate_step_internal(zip_context,
|
||||
userdata->current_file_path, &cdata, &cmode, &size, &csize, &checksum, &payload);
|
||||
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
|
||||
userdata->extracted_file_path = filename;
|
||||
userdata->crc = checksum;
|
||||
|
||||
if (file_cb && !file_cb(filename, valid_exts, cdata, cmode,
|
||||
if (file_cb && !file_cb(userdata->current_file_path, valid_exts, cdata, cmode,
|
||||
csize, size, checksum, userdata))
|
||||
return 0;
|
||||
|
||||
state->directory += payload;
|
||||
zip_context->directory_entry += payload;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void zip_parse_file_free(void *context)
|
||||
{
|
||||
zip_context_t *zip_context = (zip_context_t *)context;
|
||||
zip_context_free_stream(zip_context, false);
|
||||
free(zip_context);
|
||||
}
|
||||
|
||||
const struct file_archive_file_backend zlib_backend = {
|
||||
zlib_stream_new,
|
||||
zlib_stream_free,
|
||||
zip_parse_file_init,
|
||||
zip_parse_file_iterate_step,
|
||||
zip_parse_file_free,
|
||||
zlib_stream_decompress_data_to_file_init,
|
||||
zlib_stream_decompress_data_to_file_iterate,
|
||||
zlib_stream_crc32_calculate,
|
||||
zip_file_read,
|
||||
zip_parse_file_init,
|
||||
zip_parse_file_iterate_step,
|
||||
"zlib"
|
||||
};
|
||||
|
935
externals/libretro-common/file/config_file.c
vendored
935
externals/libretro-common/file/config_file.c
vendored
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (config_file_userdata.c).
|
||||
@ -61,6 +61,24 @@ int config_userdata_get_int(void *userdata, const char *key_str,
|
||||
return got;
|
||||
}
|
||||
|
||||
int config_userdata_get_hex(void *userdata, const char *key_str,
|
||||
unsigned *value, unsigned default_value)
|
||||
{
|
||||
bool got;
|
||||
char key[2][256];
|
||||
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
|
||||
|
||||
fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0]));
|
||||
fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1]));
|
||||
|
||||
got = config_get_hex(usr->conf, key[0], value);
|
||||
got = got || config_get_hex(usr->conf, key[1], value);
|
||||
|
||||
if (!got)
|
||||
*value = default_value;
|
||||
return got;
|
||||
}
|
||||
|
||||
int config_userdata_get_float_array(void *userdata, const char *key_str,
|
||||
float **values, unsigned *out_num_values,
|
||||
const float *default_values, unsigned num_default_values)
|
||||
@ -76,12 +94,14 @@ int config_userdata_get_float_array(void *userdata, const char *key_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);
|
||||
struct string_list list = {0};
|
||||
string_list_initialize(&list);
|
||||
string_split_noalloc(&list, str, " ");
|
||||
*values = (float*)calloc(list.size, sizeof(float));
|
||||
for (i = 0; i < list.size; i++)
|
||||
(*values)[i] = (float)strtod(list.elems[i].data, NULL);
|
||||
*out_num_values = (unsigned)list.size;
|
||||
string_list_deinitialize(&list);
|
||||
free(str);
|
||||
return true;
|
||||
}
|
||||
@ -106,12 +126,14 @@ int config_userdata_get_int_array(void *userdata, const char *key_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);
|
||||
struct string_list list = {0};
|
||||
string_list_initialize(&list);
|
||||
string_split_noalloc(&list, str, " ");
|
||||
*values = (int*)calloc(list.size, sizeof(int));
|
||||
for (i = 0; i < list.size; i++)
|
||||
(*values)[i] = (int)strtod(list.elems[i].data, NULL);
|
||||
*out_num_values = (unsigned)list.size;
|
||||
string_list_deinitialize(&list);
|
||||
free(str);
|
||||
return true;
|
||||
}
|
||||
|
317
externals/libretro-common/file/file_path.c
vendored
317
externals/libretro-common/file/file_path.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2019 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (file_path.c).
|
||||
@ -32,8 +32,7 @@
|
||||
#include <file/file_path.h>
|
||||
#include <retro_assert.h>
|
||||
#include <string/stdstring.h>
|
||||
#define VFS_FRONTEND
|
||||
#include <vfs/vfs_implementation.h>
|
||||
#include <time/rtime.h>
|
||||
|
||||
/* TODO: There are probably some unnecessary things on this huge include list now but I'm too afraid to touch it */
|
||||
#ifdef __APPLE__
|
||||
@ -46,7 +45,6 @@
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
#endif
|
||||
#include <compat/strcasestr.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <encodings/utf.h>
|
||||
|
||||
@ -82,12 +80,7 @@
|
||||
#include <pspkernel.h>
|
||||
#endif
|
||||
|
||||
#if defined(PS2)
|
||||
#include <fileXio_rpc.h>
|
||||
#include <fileXio.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#include <cell/cell_fs.h>
|
||||
#endif
|
||||
|
||||
@ -95,7 +88,7 @@
|
||||
#define FIO_S_ISDIR SCE_S_ISDIR
|
||||
#endif
|
||||
|
||||
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) || defined(PS2)
|
||||
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
|
||||
#include <unistd.h> /* stat() is defined here */
|
||||
#endif
|
||||
|
||||
@ -114,133 +107,6 @@
|
||||
|
||||
#endif
|
||||
|
||||
static retro_vfs_stat_t path_stat_cb = retro_vfs_stat_impl;
|
||||
static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl;
|
||||
|
||||
void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
|
||||
{
|
||||
const struct retro_vfs_interface*
|
||||
vfs_iface = vfs_info->iface;
|
||||
|
||||
path_stat_cb = retro_vfs_stat_impl;
|
||||
path_mkdir_cb = retro_vfs_mkdir_impl;
|
||||
|
||||
if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface)
|
||||
return;
|
||||
|
||||
path_stat_cb = vfs_iface->stat;
|
||||
path_mkdir_cb = vfs_iface->mkdir;
|
||||
}
|
||||
|
||||
int path_stat(const char *path)
|
||||
{
|
||||
return path_stat_cb(path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* path_is_directory:
|
||||
* @path : path
|
||||
*
|
||||
* Checks if path is a directory.
|
||||
*
|
||||
* Returns: true (1) if path is a directory, otherwise false (0).
|
||||
*/
|
||||
bool path_is_directory(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
bool path_is_character_special(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
|
||||
}
|
||||
|
||||
bool path_is_valid(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
|
||||
}
|
||||
|
||||
int32_t path_get_size(const char *path)
|
||||
{
|
||||
int32_t filesize = 0;
|
||||
if (path_stat_cb(path, &filesize) != 0)
|
||||
return filesize;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_mkdir:
|
||||
* @dir : directory
|
||||
*
|
||||
* Create directory on filesystem.
|
||||
*
|
||||
* Returns: true (1) if directory could be created, otherwise false (0).
|
||||
**/
|
||||
bool path_mkdir(const char *dir)
|
||||
{
|
||||
bool sret = false;
|
||||
bool norecurse = false;
|
||||
char *basedir = NULL;
|
||||
|
||||
if (!(dir && *dir))
|
||||
return false;
|
||||
|
||||
/* Use heap. Real chance of stack
|
||||
* overflow if we recurse too hard. */
|
||||
basedir = strdup(dir);
|
||||
|
||||
if (!basedir)
|
||||
return false;
|
||||
|
||||
path_parent_dir(basedir);
|
||||
|
||||
if (!*basedir || !strcmp(basedir, dir))
|
||||
{
|
||||
free(basedir);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(GEKKO)
|
||||
{
|
||||
size_t len = strlen(basedir);
|
||||
|
||||
/* path_parent_dir() keeps the trailing slash.
|
||||
* On Wii, mkdir() fails if the path has a
|
||||
* trailing slash...
|
||||
* We must therefore remove it. */
|
||||
if (len > 0)
|
||||
if (basedir[len - 1] == '/')
|
||||
basedir[len - 1] = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
if (path_is_directory(basedir))
|
||||
norecurse = true;
|
||||
else
|
||||
{
|
||||
sret = path_mkdir(basedir);
|
||||
|
||||
if (sret)
|
||||
norecurse = true;
|
||||
}
|
||||
|
||||
free(basedir);
|
||||
|
||||
if (norecurse)
|
||||
{
|
||||
int ret = path_mkdir_cb(dir);
|
||||
|
||||
/* Don't treat this as an error. */
|
||||
if (ret == -2 && path_is_directory(dir))
|
||||
return true;
|
||||
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
return sret;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_get_archive_delim:
|
||||
* @path : path
|
||||
@ -253,26 +119,48 @@ bool path_mkdir(const char *dir)
|
||||
*/
|
||||
const char *path_get_archive_delim(const char *path)
|
||||
{
|
||||
const char *last = find_last_slash(path);
|
||||
const char *delim = NULL;
|
||||
const char *last_slash = find_last_slash(path);
|
||||
const char *delim = NULL;
|
||||
char buf[5];
|
||||
|
||||
if (!last)
|
||||
buf[0] = '\0';
|
||||
|
||||
if (!last_slash)
|
||||
return NULL;
|
||||
|
||||
/* Test if it's .zip */
|
||||
delim = strcasestr(last, ".zip#");
|
||||
/* Find delimiter position */
|
||||
delim = strrchr(last_slash, '#');
|
||||
|
||||
if (!delim) /* If it's not a .zip, test if it's .apk */
|
||||
delim = strcasestr(last, ".apk#");
|
||||
if (!delim)
|
||||
return NULL;
|
||||
|
||||
if (delim)
|
||||
return delim + 4;
|
||||
/* Check whether this is a known archive type
|
||||
* > Note: The code duplication here is
|
||||
* deliberate, to maximise performance */
|
||||
if (delim - last_slash > 4)
|
||||
{
|
||||
strlcpy(buf, delim - 4, sizeof(buf));
|
||||
buf[4] = '\0';
|
||||
|
||||
/* If it's not a .zip or .apk file, test if it's .7z */
|
||||
delim = strcasestr(last, ".7z#");
|
||||
string_to_lower(buf);
|
||||
|
||||
if (delim)
|
||||
return delim + 3;
|
||||
/* Check if this is a '.zip', '.apk' or '.7z' file */
|
||||
if (string_is_equal(buf, ".zip") ||
|
||||
string_is_equal(buf, ".apk") ||
|
||||
string_is_equal(buf + 1, ".7z"))
|
||||
return delim;
|
||||
}
|
||||
else if (delim - last_slash > 3)
|
||||
{
|
||||
strlcpy(buf, delim - 3, sizeof(buf));
|
||||
buf[3] = '\0';
|
||||
|
||||
string_to_lower(buf);
|
||||
|
||||
/* Check if this is a '.7z' file */
|
||||
if (string_is_equal(buf, ".7z"))
|
||||
return delim;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -331,9 +219,12 @@ bool path_is_compressed_file(const char* path)
|
||||
{
|
||||
const char *ext = path_get_extension(path);
|
||||
|
||||
if ( strcasestr(ext, "zip")
|
||||
|| strcasestr(ext, "apk")
|
||||
|| strcasestr(ext, "7z"))
|
||||
if (string_is_empty(ext))
|
||||
return false;
|
||||
|
||||
if (string_is_equal_noncase(ext, "zip") ||
|
||||
string_is_equal_noncase(ext, "apk") ||
|
||||
string_is_equal_noncase(ext, "7z"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@ -423,7 +314,7 @@ void fill_pathname_slash(char *path, size_t size)
|
||||
|
||||
if (!last_slash)
|
||||
{
|
||||
strlcat(path, path_default_slash(), size);
|
||||
strlcat(path, PATH_DEFAULT_SLASH(), size);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -592,11 +483,13 @@ void fill_pathname_parent_dir(char *out_dir,
|
||||
size_t fill_dated_filename(char *out_filename,
|
||||
const char *ext, size_t size)
|
||||
{
|
||||
time_t cur_time = time(NULL);
|
||||
const struct tm* tm_ = localtime(&cur_time);
|
||||
time_t cur_time = time(NULL);
|
||||
struct tm tm_;
|
||||
|
||||
rtime_localtime(&cur_time, &tm_);
|
||||
|
||||
strftime(out_filename, size,
|
||||
"RetroArch-%m%d-%H%M%S", tm_);
|
||||
"RetroArch-%m%d-%H%M%S", &tm_);
|
||||
return strlcat(out_filename, ext, size);
|
||||
}
|
||||
|
||||
@ -617,19 +510,21 @@ void fill_str_dated_filename(char *out_filename,
|
||||
const char *in_str, const char *ext, size_t size)
|
||||
{
|
||||
char format[256];
|
||||
time_t cur_time = time(NULL);
|
||||
const struct tm* tm_ = localtime(&cur_time);
|
||||
struct tm tm_;
|
||||
time_t cur_time = time(NULL);
|
||||
|
||||
format[0] = '\0';
|
||||
format[0] = '\0';
|
||||
|
||||
rtime_localtime(&cur_time, &tm_);
|
||||
|
||||
if (string_is_empty(ext))
|
||||
{
|
||||
strftime(format, sizeof(format), "-%y%m%d-%H%M%S", tm_);
|
||||
strftime(format, sizeof(format), "-%y%m%d-%H%M%S", &tm_);
|
||||
fill_pathname_noext(out_filename, in_str, format, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", tm_);
|
||||
strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", &tm_);
|
||||
|
||||
fill_pathname_join_concat_noext(out_filename,
|
||||
in_str, format, ext,
|
||||
@ -656,7 +551,7 @@ void path_basedir(char *path)
|
||||
if (last)
|
||||
last[1] = '\0';
|
||||
else
|
||||
snprintf(path, 3, ".%s", path_default_slash());
|
||||
snprintf(path, 3, "." PATH_DEFAULT_SLASH());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -676,7 +571,7 @@ void path_parent_dir(char *path)
|
||||
|
||||
len = strlen(path);
|
||||
|
||||
if (len && path_char_is_slash(path[len - 1]))
|
||||
if (len && PATH_CHAR_IS_SLASH(path[len - 1]))
|
||||
{
|
||||
bool path_was_absolute = path_is_absolute(path);
|
||||
|
||||
@ -731,19 +626,27 @@ const char *path_basename(const char *path)
|
||||
**/
|
||||
bool path_is_absolute(const char *path)
|
||||
{
|
||||
if (string_is_empty(path))
|
||||
return false;
|
||||
|
||||
if (path[0] == '/')
|
||||
return true;
|
||||
#ifdef _WIN32
|
||||
/* Many roads lead to Rome ... */
|
||||
if (( strstr(path, "\\\\") == path)
|
||||
|| strstr(path, ":/")
|
||||
|| strstr(path, ":\\")
|
||||
|| strstr(path, ":\\\\"))
|
||||
return true;
|
||||
#elif defined(__wiiu__)
|
||||
if (strstr(path, ":/"))
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* Many roads lead to Rome...
|
||||
* Note: Drive letter can only be 1 character long */
|
||||
if (string_starts_with_size(path, "\\\\", STRLEN_CONST("\\\\")) ||
|
||||
string_starts_with_size(path + 1, ":/", STRLEN_CONST(":/")) ||
|
||||
string_starts_with_size(path + 1, ":\\", STRLEN_CONST(":\\")))
|
||||
return true;
|
||||
#elif defined(__wiiu__) || defined(VITA)
|
||||
{
|
||||
const char *seperator = strchr(path, ':');
|
||||
if (seperator && (seperator[1] == '/'))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -893,7 +796,7 @@ end:
|
||||
size_t path_relative_to(char *out,
|
||||
const char *path, const char *base, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
size_t i, j;
|
||||
const char *trimmed_path, *trimmed_base;
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -905,16 +808,18 @@ size_t path_relative_to(char *out,
|
||||
#endif
|
||||
|
||||
/* Trim common beginning */
|
||||
for (i = 0; path[i] && base[i] && path[i] == base[i]; )
|
||||
i++;
|
||||
trimmed_path = path+i;
|
||||
for (i = 0, j = 0; path[i] && base[i] && path[i] == base[i]; i++)
|
||||
if (path[i] == PATH_DEFAULT_SLASH_C())
|
||||
j = i + 1;
|
||||
|
||||
trimmed_path = path+j;
|
||||
trimmed_base = base+i;
|
||||
|
||||
/* Each segment of base turns into ".." */
|
||||
out[0] = '\0';
|
||||
for (i = 0; trimmed_base[i]; i++)
|
||||
if (trimmed_base[i] == path_default_slash_c())
|
||||
strlcat(out, ".." path_default_slash(), size);
|
||||
if (trimmed_base[i] == PATH_DEFAULT_SLASH_C())
|
||||
strlcat(out, ".." PATH_DEFAULT_SLASH(), size);
|
||||
|
||||
return strlcat(out, trimmed_path, size);
|
||||
}
|
||||
@ -1099,13 +1004,13 @@ void fill_pathname_expand_special(char *out_path,
|
||||
out_path += src_size;
|
||||
size -= src_size;
|
||||
|
||||
if (!path_char_is_slash(out_path[-1]))
|
||||
if (!PATH_CHAR_IS_SLASH(out_path[-1]))
|
||||
{
|
||||
src_size = strlcpy(out_path, path_default_slash(), size);
|
||||
src_size = strlcpy(out_path, PATH_DEFAULT_SLASH(), size);
|
||||
retro_assert(src_size < size);
|
||||
|
||||
out_path += src_size;
|
||||
size -= src_size;
|
||||
size -= src_size;
|
||||
}
|
||||
|
||||
in_path += 2;
|
||||
@ -1130,9 +1035,9 @@ void fill_pathname_expand_special(char *out_path,
|
||||
out_path += src_size;
|
||||
size -= src_size;
|
||||
|
||||
if (!path_char_is_slash(out_path[-1]))
|
||||
if (!PATH_CHAR_IS_SLASH(out_path[-1]))
|
||||
{
|
||||
src_size = strlcpy(out_path, path_default_slash(), size);
|
||||
src_size = strlcpy(out_path, PATH_DEFAULT_SLASH(), size);
|
||||
retro_assert(src_size < size);
|
||||
|
||||
out_path += src_size;
|
||||
@ -1156,10 +1061,11 @@ void fill_pathname_abbreviate_special(char *out_path,
|
||||
unsigned i;
|
||||
const char *candidates[3];
|
||||
const char *notations[3];
|
||||
char *application_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
|
||||
char *home_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
|
||||
char application_dir[PATH_MAX_LENGTH];
|
||||
char home_dir[PATH_MAX_LENGTH];
|
||||
|
||||
application_dir[0] = '\0';
|
||||
home_dir[0] = '\0';
|
||||
|
||||
/* application_dir could be zero-string. Safeguard against this.
|
||||
*
|
||||
@ -1176,15 +1082,13 @@ void fill_pathname_abbreviate_special(char *out_path,
|
||||
notations [1] = "~";
|
||||
notations [2] = NULL;
|
||||
|
||||
fill_pathname_application_dir(application_dir,
|
||||
PATH_MAX_LENGTH * sizeof(char));
|
||||
fill_pathname_home_dir(home_dir,
|
||||
PATH_MAX_LENGTH * sizeof(char));
|
||||
fill_pathname_application_dir(application_dir, sizeof(application_dir));
|
||||
fill_pathname_home_dir(home_dir, sizeof(home_dir));
|
||||
|
||||
for (i = 0; candidates[i]; i++)
|
||||
{
|
||||
if (!string_is_empty(candidates[i]) &&
|
||||
strstr(in_path, candidates[i]) == in_path)
|
||||
string_starts_with(in_path, candidates[i]))
|
||||
{
|
||||
size_t src_size = strlcpy(out_path, notations[i], size);
|
||||
|
||||
@ -1194,10 +1098,9 @@ void fill_pathname_abbreviate_special(char *out_path,
|
||||
size -= src_size;
|
||||
in_path += strlen(candidates[i]);
|
||||
|
||||
if (!path_char_is_slash(*in_path))
|
||||
if (!PATH_CHAR_IS_SLASH(*in_path))
|
||||
{
|
||||
retro_assert(strlcpy(out_path,
|
||||
path_default_slash(), size) < size);
|
||||
strcpy_literal(out_path, PATH_DEFAULT_SLASH());
|
||||
out_path++;
|
||||
size--;
|
||||
}
|
||||
@ -1206,8 +1109,6 @@ void fill_pathname_abbreviate_special(char *out_path,
|
||||
}
|
||||
}
|
||||
|
||||
free(application_dir);
|
||||
free(home_dir);
|
||||
#endif
|
||||
|
||||
retro_assert(strlcpy(out_path, in_path, size) < size);
|
||||
@ -1238,7 +1139,7 @@ void path_basedir_wrapper(char *path)
|
||||
if (last)
|
||||
last[1] = '\0';
|
||||
else
|
||||
snprintf(path, 3, ".%s", path_default_slash());
|
||||
snprintf(path, 3, "." PATH_DEFAULT_SLASH());
|
||||
}
|
||||
|
||||
#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
|
||||
@ -1365,29 +1266,27 @@ void fill_pathname_application_dir(char *s, size_t len)
|
||||
void fill_pathname_home_dir(char *s, size_t len)
|
||||
{
|
||||
#ifdef __WINRT__
|
||||
strlcpy(s, uwp_dir_data, len);
|
||||
const char *home = uwp_dir_data;
|
||||
#else
|
||||
const char *home = getenv("HOME");
|
||||
#endif
|
||||
if (home)
|
||||
strlcpy(s, home, len);
|
||||
else
|
||||
*s = 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
bool is_path_accessible_using_standard_io(const char *path)
|
||||
{
|
||||
#ifdef __WINRT__
|
||||
bool result;
|
||||
size_t path_sizeof = PATH_MAX_LENGTH * sizeof(char);
|
||||
char *relative_path_abbrev = (char*)malloc(path_sizeof);
|
||||
fill_pathname_abbreviate_special(relative_path_abbrev, path, path_sizeof);
|
||||
|
||||
result = strlen(relative_path_abbrev) >= 2 && (relative_path_abbrev[0] == ':' || relative_path_abbrev[0] == '~') && path_char_is_slash(relative_path_abbrev[1]);
|
||||
|
||||
free(relative_path_abbrev);
|
||||
return result;
|
||||
char relative_path_abbrev[PATH_MAX_LENGTH];
|
||||
fill_pathname_abbreviate_special(relative_path_abbrev,
|
||||
path, sizeof(relative_path_abbrev));
|
||||
return (strlen(relative_path_abbrev) >= 2 )
|
||||
&& ( relative_path_abbrev[0] == ':'
|
||||
|| relative_path_abbrev[0] == '~')
|
||||
&& PATH_CHAR_IS_SLASH(relative_path_abbrev[1]);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
|
224
externals/libretro-common/file/file_path_io.c
vendored
Normal file
224
externals/libretro-common/file/file_path_io.c
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (file_path_io.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <file/file_path.h>
|
||||
#include <retro_assert.h>
|
||||
#include <string/stdstring.h>
|
||||
#define VFS_FRONTEND
|
||||
#include <vfs/vfs_implementation.h>
|
||||
|
||||
/* TODO: There are probably some unnecessary things on this huge include list now but I'm too afraid to touch it */
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
#ifdef __HAIKU__
|
||||
#include <kernel/image.h>
|
||||
#endif
|
||||
#ifndef __MACH__
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
#endif
|
||||
#include <compat/strcasestr.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <encodings/utf.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>
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(VITA)
|
||||
#define SCE_ERROR_ERRNO_EEXIST 0x80010011
|
||||
#include <psp2/io/fcntl.h>
|
||||
#include <psp2/io/dirent.h>
|
||||
#include <psp2/io/stat.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(PSP)
|
||||
#include <pspkernel.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#include <cell/cell_fs.h>
|
||||
#endif
|
||||
|
||||
#if defined(VITA)
|
||||
#define FIO_S_ISDIR SCE_S_ISDIR
|
||||
#endif
|
||||
|
||||
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) || defined(PS2)
|
||||
#include <unistd.h> /* stat() is defined here */
|
||||
#endif
|
||||
|
||||
#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
|
||||
#ifdef __WINRT__
|
||||
#include <uwp/uwp_func.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Assume W-functions do not work below Win2K and Xbox platforms */
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||
|
||||
#ifndef LEGACY_WIN32
|
||||
#define LEGACY_WIN32
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* TODO/FIXME - globals */
|
||||
static retro_vfs_stat_t path_stat_cb = retro_vfs_stat_impl;
|
||||
static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl;
|
||||
|
||||
void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
|
||||
{
|
||||
const struct retro_vfs_interface*
|
||||
vfs_iface = vfs_info->iface;
|
||||
|
||||
path_stat_cb = retro_vfs_stat_impl;
|
||||
path_mkdir_cb = retro_vfs_mkdir_impl;
|
||||
|
||||
if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface)
|
||||
return;
|
||||
|
||||
path_stat_cb = vfs_iface->stat;
|
||||
path_mkdir_cb = vfs_iface->mkdir;
|
||||
}
|
||||
|
||||
int path_stat(const char *path)
|
||||
{
|
||||
return path_stat_cb(path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* path_is_directory:
|
||||
* @path : path
|
||||
*
|
||||
* Checks if path is a directory.
|
||||
*
|
||||
* Returns: true (1) if path is a directory, otherwise false (0).
|
||||
*/
|
||||
bool path_is_directory(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
bool path_is_character_special(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
|
||||
}
|
||||
|
||||
bool path_is_valid(const char *path)
|
||||
{
|
||||
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
|
||||
}
|
||||
|
||||
int32_t path_get_size(const char *path)
|
||||
{
|
||||
int32_t filesize = 0;
|
||||
if (path_stat_cb(path, &filesize) != 0)
|
||||
return filesize;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* path_mkdir:
|
||||
* @dir : directory
|
||||
*
|
||||
* Create directory on filesystem.
|
||||
*
|
||||
* Returns: true (1) if directory could be created, otherwise false (0).
|
||||
**/
|
||||
bool path_mkdir(const char *dir)
|
||||
{
|
||||
bool sret = false;
|
||||
bool norecurse = false;
|
||||
char *basedir = NULL;
|
||||
|
||||
if (!(dir && *dir))
|
||||
return false;
|
||||
|
||||
/* Use heap. Real chance of stack
|
||||
* overflow if we recurse too hard. */
|
||||
basedir = strdup(dir);
|
||||
|
||||
if (!basedir)
|
||||
return false;
|
||||
|
||||
path_parent_dir(basedir);
|
||||
|
||||
if (!*basedir || !strcmp(basedir, dir))
|
||||
{
|
||||
free(basedir);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path_is_directory(basedir))
|
||||
norecurse = true;
|
||||
else
|
||||
{
|
||||
sret = path_mkdir(basedir);
|
||||
|
||||
if (sret)
|
||||
norecurse = true;
|
||||
}
|
||||
|
||||
free(basedir);
|
||||
|
||||
if (norecurse)
|
||||
{
|
||||
int ret = path_mkdir_cb(dir);
|
||||
|
||||
/* Don't treat this as an error. */
|
||||
if (ret == -2 && path_is_directory(dir))
|
||||
return true;
|
||||
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
return sret;
|
||||
}
|
22
externals/libretro-common/file/nbio/nbio_intf.c
vendored
22
externals/libretro-common/file/nbio/nbio_intf.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nbio_intf.c).
|
||||
@ -37,11 +37,29 @@ extern nbio_intf_t nbio_orbis;
|
||||
#endif
|
||||
extern nbio_intf_t nbio_stdio;
|
||||
|
||||
#ifndef _XBOX
|
||||
#if defined(_WIN32)
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1500
|
||||
|
||||
#ifndef HAVE_MMAP_WIN32
|
||||
#define HAVE_MMAP_WIN32
|
||||
#endif
|
||||
|
||||
#elif !defined(_MSC_VER)
|
||||
|
||||
#ifndef HAVE_MMAP_WIN32
|
||||
#define HAVE_MMAP_WIN32
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_linux__)
|
||||
static nbio_intf_t *internal_nbio = &nbio_linux;
|
||||
#elif defined(HAVE_MMAP) && defined(BSD)
|
||||
static nbio_intf_t *internal_nbio = &nbio_mmap_unix;
|
||||
#elif defined(_WIN32) && !defined(_XBOX)
|
||||
#elif defined(HAVE_MMAP_WIN32)
|
||||
static nbio_intf_t *internal_nbio = &nbio_mmap_win32;
|
||||
#elif defined(ORBIS)
|
||||
static nbio_intf_t *internal_nbio = &nbio_orbis;
|
||||
|
11
externals/libretro-common/file/nbio/nbio_linux.c
vendored
11
externals/libretro-common/file/nbio/nbio_linux.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nbio_linux.c).
|
||||
@ -31,6 +31,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@ -39,14 +40,12 @@
|
||||
|
||||
struct nbio_linux_t
|
||||
{
|
||||
int fd;
|
||||
bool busy;
|
||||
|
||||
void* ptr;
|
||||
aio_context_t ctx;
|
||||
struct iocb cb;
|
||||
|
||||
void* ptr;
|
||||
size_t len;
|
||||
int fd;
|
||||
bool busy;
|
||||
};
|
||||
|
||||
/* there's also a Unix AIO thingy, but it's not in glibc
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nbio_orbis.c).
|
||||
@ -31,10 +31,11 @@
|
||||
|
||||
struct nbio_orbis_t
|
||||
{
|
||||
int fd;
|
||||
void* data;
|
||||
size_t progress;
|
||||
size_t len;
|
||||
int fd;
|
||||
unsigned int mode;
|
||||
/*
|
||||
* possible values:
|
||||
* NBIO_READ, NBIO_WRITE - obvious
|
||||
@ -42,7 +43,6 @@ struct nbio_orbis_t
|
||||
* -2 - the pointer was reallocated since the last operation
|
||||
*/
|
||||
signed char op;
|
||||
unsigned int mode;
|
||||
};
|
||||
|
||||
static void *nbio_orbis_open(const char * filename, unsigned int mode)
|
||||
|
50
externals/libretro-common/file/nbio/nbio_stdio.c
vendored
50
externals/libretro-common/file/nbio/nbio_stdio.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nbio_stdio.c).
|
||||
@ -35,6 +35,18 @@
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||
#define ATLEAST_VC2005
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE - 0) >= 200112) || (defined(__POSIX_VISIBLE) && __POSIX_VISIBLE >= 200112) || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || __USE_LARGEFILE || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
|
||||
#ifndef HAVE_64BIT_OFFSETS
|
||||
#define HAVE_64BIT_OFFSETS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct nbio_stdio_t
|
||||
{
|
||||
FILE* f;
|
||||
@ -57,11 +69,35 @@ static const char *stdio_modes[] = { "rb", "wb", "r+b", "rb", "wb", "r+b" };
|
||||
static const wchar_t *stdio_modes[] = { L"rb", L"wb", L"r+b", L"rb", L"wb", L"r+b" };
|
||||
#endif
|
||||
|
||||
static int64_t fseek_wrap(FILE *f, int64_t offset, int origin)
|
||||
{
|
||||
#ifdef ATLEAST_VC2005
|
||||
/* VC2005 and up have a special 64-bit fseek */
|
||||
return _fseeki64(f, offset, origin);
|
||||
#elif defined(HAVE_64BIT_OFFSETS)
|
||||
return fseeko(f, (off_t)offset, origin);
|
||||
#else
|
||||
return fseek(f, (long)offset, origin);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int64_t ftell_wrap(FILE *f)
|
||||
{
|
||||
#ifdef ATLEAST_VC2005
|
||||
/* VC2005 and up have a special 64-bit ftell */
|
||||
return _ftelli64(f);
|
||||
#elif defined(HAVE_64BIT_OFFSETS)
|
||||
return ftello(f);
|
||||
#else
|
||||
return ftell(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *nbio_stdio_open(const char * filename, unsigned mode)
|
||||
{
|
||||
void *buf = NULL;
|
||||
struct nbio_stdio_t* handle = NULL;
|
||||
size_t len = 0;
|
||||
int64_t len = 0;
|
||||
#if !defined(_WIN32) || defined(LEGACY_WIN32)
|
||||
FILE* f = fopen(filename, stdio_modes[mode]);
|
||||
#else
|
||||
@ -87,15 +123,15 @@ static void *nbio_stdio_open(const char * filename, unsigned mode)
|
||||
case BIO_WRITE:
|
||||
break;
|
||||
default:
|
||||
fseek(handle->f, 0, SEEK_END);
|
||||
len = ftell(handle->f);
|
||||
fseek_wrap(handle->f, 0, SEEK_END);
|
||||
len = ftell_wrap(handle->f);
|
||||
break;
|
||||
}
|
||||
|
||||
handle->mode = mode;
|
||||
|
||||
if (len)
|
||||
buf = malloc(len);
|
||||
buf = malloc((size_t)len);
|
||||
|
||||
if (len && !buf)
|
||||
goto error;
|
||||
@ -123,7 +159,7 @@ static void nbio_stdio_begin_read(void *data)
|
||||
if (handle->op >= 0)
|
||||
abort();
|
||||
|
||||
fseek(handle->f, 0, SEEK_SET);
|
||||
fseek_wrap(handle->f, 0, SEEK_SET);
|
||||
|
||||
handle->op = NBIO_READ;
|
||||
handle->progress = 0;
|
||||
@ -138,7 +174,7 @@ static void nbio_stdio_begin_write(void *data)
|
||||
if (handle->op >= 0)
|
||||
abort();
|
||||
|
||||
fseek(handle->f, 0, SEEK_SET);
|
||||
fseek_wrap(handle->f, 0, SEEK_SET);
|
||||
handle->op = NBIO_WRITE;
|
||||
handle->progress = 0;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nbio_unixmmap.c).
|
||||
@ -55,10 +55,10 @@
|
||||
|
||||
struct nbio_mmap_unix_t
|
||||
{
|
||||
void* ptr;
|
||||
size_t len;
|
||||
int fd;
|
||||
int map_flags;
|
||||
size_t len;
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
static void *nbio_mmap_unix_open(const char * filename, unsigned mode)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nbio_windowsmmap.c).
|
||||
@ -22,7 +22,23 @@
|
||||
|
||||
#include <file/nbio.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(_XBOX)
|
||||
#if defined(_WIN32)
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1500
|
||||
|
||||
#ifndef HAVE_MMAP_WIN32
|
||||
#define HAVE_MMAP_WIN32
|
||||
#endif
|
||||
|
||||
#elif !defined(_MSC_VER)
|
||||
|
||||
#ifndef HAVE_MMAP_WIN32
|
||||
#define HAVE_MMAP_WIN32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MMAP_WIN32)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -47,9 +63,9 @@
|
||||
struct nbio_mmap_win32_t
|
||||
{
|
||||
HANDLE file;
|
||||
bool is_write;
|
||||
size_t len;
|
||||
void* ptr;
|
||||
size_t len;
|
||||
bool is_write;
|
||||
};
|
||||
|
||||
static void *nbio_mmap_win32_open(const char * filename, unsigned mode)
|
||||
|
46
externals/libretro-common/file/retro_dirent.c
vendored
46
externals/libretro-common/file/retro_dirent.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2019 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (retro_dirent.c).
|
||||
@ -31,37 +31,41 @@
|
||||
#define VFS_FRONTEND
|
||||
#include <vfs/vfs_implementation.h>
|
||||
|
||||
static retro_vfs_opendir_t dirent_opendir_cb = NULL;
|
||||
static retro_vfs_readdir_t dirent_readdir_cb = NULL;
|
||||
/* TODO/FIXME - static globals */
|
||||
static retro_vfs_opendir_t dirent_opendir_cb = NULL;
|
||||
static retro_vfs_readdir_t dirent_readdir_cb = NULL;
|
||||
static retro_vfs_dirent_get_name_t dirent_dirent_get_name_cb = NULL;
|
||||
static retro_vfs_dirent_is_dir_t dirent_dirent_is_dir_cb = NULL;
|
||||
static retro_vfs_closedir_t dirent_closedir_cb = NULL;
|
||||
static retro_vfs_dirent_is_dir_t dirent_dirent_is_dir_cb = NULL;
|
||||
static retro_vfs_closedir_t dirent_closedir_cb = NULL;
|
||||
|
||||
void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info)
|
||||
{
|
||||
const struct retro_vfs_interface* vfs_iface;
|
||||
|
||||
dirent_opendir_cb = NULL;
|
||||
dirent_readdir_cb = NULL;
|
||||
dirent_opendir_cb = NULL;
|
||||
dirent_readdir_cb = NULL;
|
||||
dirent_dirent_get_name_cb = NULL;
|
||||
dirent_dirent_is_dir_cb = NULL;
|
||||
dirent_closedir_cb = NULL;
|
||||
dirent_dirent_is_dir_cb = NULL;
|
||||
dirent_closedir_cb = NULL;
|
||||
|
||||
vfs_iface = vfs_info->iface;
|
||||
vfs_iface = vfs_info->iface;
|
||||
|
||||
if (vfs_info->required_interface_version < DIRENT_REQUIRED_VFS_VERSION || !vfs_iface)
|
||||
if (
|
||||
vfs_info->required_interface_version < DIRENT_REQUIRED_VFS_VERSION ||
|
||||
!vfs_iface)
|
||||
return;
|
||||
|
||||
dirent_opendir_cb = vfs_iface->opendir;
|
||||
dirent_readdir_cb = vfs_iface->readdir;
|
||||
dirent_opendir_cb = vfs_iface->opendir;
|
||||
dirent_readdir_cb = vfs_iface->readdir;
|
||||
dirent_dirent_get_name_cb = vfs_iface->dirent_get_name;
|
||||
dirent_dirent_is_dir_cb = vfs_iface->dirent_is_dir;
|
||||
dirent_closedir_cb = vfs_iface->closedir;
|
||||
dirent_dirent_is_dir_cb = vfs_iface->dirent_is_dir;
|
||||
dirent_closedir_cb = vfs_iface->closedir;
|
||||
}
|
||||
|
||||
struct RDIR *retro_opendir_include_hidden(const char *name, bool include_hidden)
|
||||
struct RDIR *retro_opendir_include_hidden(
|
||||
const char *name, bool include_hidden)
|
||||
{
|
||||
if (dirent_opendir_cb != NULL)
|
||||
if (dirent_opendir_cb)
|
||||
return (struct RDIR *)dirent_opendir_cb(name, include_hidden);
|
||||
return (struct RDIR *)retro_vfs_opendir_impl(name, include_hidden);
|
||||
}
|
||||
@ -79,14 +83,14 @@ bool retro_dirent_error(struct RDIR *rdir)
|
||||
|
||||
int retro_readdir(struct RDIR *rdir)
|
||||
{
|
||||
if (dirent_readdir_cb != NULL)
|
||||
if (dirent_readdir_cb)
|
||||
return dirent_readdir_cb((struct retro_vfs_dir_handle *)rdir);
|
||||
return retro_vfs_readdir_impl((struct retro_vfs_dir_handle *)rdir);
|
||||
}
|
||||
|
||||
const char *retro_dirent_get_name(struct RDIR *rdir)
|
||||
{
|
||||
if (dirent_dirent_get_name_cb != NULL)
|
||||
if (dirent_dirent_get_name_cb)
|
||||
return dirent_dirent_get_name_cb((struct retro_vfs_dir_handle *)rdir);
|
||||
return retro_vfs_dirent_get_name_impl((struct retro_vfs_dir_handle *)rdir);
|
||||
}
|
||||
@ -104,14 +108,14 @@ const char *retro_dirent_get_name(struct RDIR *rdir)
|
||||
*/
|
||||
bool retro_dirent_is_dir(struct RDIR *rdir, const char *unused)
|
||||
{
|
||||
if (dirent_dirent_is_dir_cb != NULL)
|
||||
if (dirent_dirent_is_dir_cb)
|
||||
return dirent_dirent_is_dir_cb((struct retro_vfs_dir_handle *)rdir);
|
||||
return retro_vfs_dirent_is_dir_impl((struct retro_vfs_dir_handle *)rdir);
|
||||
}
|
||||
|
||||
void retro_closedir(struct RDIR *rdir)
|
||||
{
|
||||
if (dirent_closedir_cb != NULL)
|
||||
if (dirent_closedir_cb)
|
||||
dirent_closedir_cb((struct retro_vfs_dir_handle *)rdir);
|
||||
else
|
||||
retro_vfs_closedir_impl((struct retro_vfs_dir_handle *)rdir);
|
||||
|
42
externals/libretro-common/formats/bmp/rbmp.c
vendored
42
externals/libretro-common/formats/bmp/rbmp.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rbmp.c).
|
||||
@ -41,17 +41,15 @@
|
||||
|
||||
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;
|
||||
int img_n;
|
||||
int img_out_n;
|
||||
int buflen;
|
||||
uint32_t img_x;
|
||||
uint32_t img_y;
|
||||
unsigned char buffer_start[128];
|
||||
} rbmp_context;
|
||||
|
||||
struct rbmp
|
||||
@ -107,40 +105,40 @@ static unsigned char *rbmp_convert_format(
|
||||
switch (((img_n)*8+(req_comp)))
|
||||
{
|
||||
case 10:
|
||||
for(i = x-1; i >= 0; --i, src += 1, dest += 2)
|
||||
for (i = x-1; i >= 0; --i, src += 1, dest += 2)
|
||||
{
|
||||
dest[0]=src[0];
|
||||
dest[1]=255;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
for(i = x-1; i >= 0; --i, src += 1, dest += 3)
|
||||
for (i = x-1; i >= 0; --i, src += 1, dest += 3)
|
||||
dest[0]=dest[1]=dest[2]=src[0];
|
||||
break;
|
||||
case 12:
|
||||
for(i = x-1; i >= 0; --i, src += 1, dest += 4)
|
||||
for (i = x-1; i >= 0; --i, src += 1, dest += 4)
|
||||
{
|
||||
dest[0]=dest[1]=dest[2]=src[0];
|
||||
dest[3]=255;
|
||||
}
|
||||
break;
|
||||
case 17:
|
||||
for(i = x-1; i >= 0; --i, src += 2, dest += 1)
|
||||
for (i = x-1; i >= 0; --i, src += 2, dest += 1)
|
||||
dest[0]=src[0];
|
||||
break;
|
||||
case 19:
|
||||
for(i = x-1; i >= 0; --i, src += 2, dest += 3)
|
||||
for (i = x-1; i >= 0; --i, src += 2, dest += 3)
|
||||
dest[0]=dest[1]=dest[2]=src[0];
|
||||
break;
|
||||
case 20:
|
||||
for(i = x-1; i >= 0; --i, src += 2, dest += 4)
|
||||
for (i = x-1; i >= 0; --i, src += 2, dest += 4)
|
||||
{
|
||||
dest[0]=dest[1]=dest[2]=src[0];
|
||||
dest[3]=src[1];
|
||||
}
|
||||
break;
|
||||
case 28:
|
||||
for(i = x-1; i >= 0; --i, src += 3, dest += 4)
|
||||
for (i = x-1; i >= 0; --i, src += 3, dest += 4)
|
||||
{
|
||||
dest[0]=src[0];
|
||||
dest[1]=src[1];
|
||||
@ -149,29 +147,29 @@ static unsigned char *rbmp_convert_format(
|
||||
}
|
||||
break;
|
||||
case 25:
|
||||
for(i = x-1; i >= 0; --i, src += 3, dest += 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 26:
|
||||
for(i = x-1; i >= 0; --i, src += 3, dest += 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 33:
|
||||
for(i = x-1; i >= 0; --i, src += 4, dest += 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 34:
|
||||
for(i = x-1; i >= 0; --i, src += 4, dest += 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 35:
|
||||
for(i = x-1; i >= 0; --i, src += 4, dest += 3)
|
||||
for (i = x-1; i >= 0; --i, src += 4, dest += 3)
|
||||
{
|
||||
dest[0]=src[0];
|
||||
dest[1]=src[1];
|
||||
@ -735,7 +733,7 @@ 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)
|
||||
while (frame < end)
|
||||
{
|
||||
uint32_t pixel = *frame;
|
||||
*frame = (pixel & 0xff00ff00) | ((pixel << 16) & 0x00ff0000) | ((pixel >> 16) & 0xff);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rbmp_encode.c).
|
||||
|
630
externals/libretro-common/formats/cdfs/cdfs.c
vendored
Normal file
630
externals/libretro-common/formats/cdfs/cdfs.c
vendored
Normal file
@ -0,0 +1,630 @@
|
||||
#include <formats/cdfs.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <compat/strl.h>
|
||||
#include <file/file_path.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
#include <streams/chd_stream.h>
|
||||
#endif
|
||||
|
||||
static void cdfs_determine_sector_size(cdfs_track_t* track)
|
||||
{
|
||||
uint8_t buffer[32];
|
||||
const int toc_sector = 16;
|
||||
|
||||
/* MODE information is normally found in the CUE sheet, but we can try to determine it from the raw data.
|
||||
*
|
||||
* MODE1/2048 - CDROM Mode1 Data (cooked) [no header, no footer]
|
||||
* MODE1/2352 - CDROM Mode1 Data (raw) [16 byte header, 288 byte footer]
|
||||
* MODE2/2336 - CDROM-XA Mode2 Data [8 byte header, 280 byte footer]
|
||||
* MODE2/2352 - CDROM-XA Mode2 Data [24 byte header, 280 byte footer]
|
||||
*
|
||||
* Note that MODE is actually a property on each sector and can change between 1 and 2 depending on how much error
|
||||
* correction the author desired. To support that, the data format must be "/2352" to include the full header and
|
||||
* data without error correction information, at which point the CUE sheet information becomes just a hint.
|
||||
*/
|
||||
|
||||
/* The boot record or primary volume descriptor is always at sector 16 and will contain a "CD001" marker */
|
||||
intfstream_seek(track->stream, toc_sector * 2352 + track->first_sector_offset, SEEK_SET);
|
||||
if (intfstream_read(track->stream, buffer, sizeof(buffer)) < sizeof(buffer))
|
||||
return;
|
||||
|
||||
/* if this is a CDROM-XA data source, the "CD001" tag will be 25 bytes into the sector */
|
||||
if (buffer[25] == 0x43 && buffer[26] == 0x44 &&
|
||||
buffer[27] == 0x30 && buffer[28] == 0x30 && buffer[29] == 0x31)
|
||||
{
|
||||
track->stream_sector_size = 2352;
|
||||
track->stream_sector_header_size = 24;
|
||||
}
|
||||
/* otherwise it should be 17 bytes into the sector */
|
||||
else if (buffer[17] == 0x43 && buffer[18] == 0x44 &&
|
||||
buffer[19] == 0x30 && buffer[20] == 0x30 && buffer[21] == 0x31)
|
||||
{
|
||||
track->stream_sector_size = 2352;
|
||||
track->stream_sector_header_size = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ISO-9660 says the first twelve bytes of a sector should be the sync pattern 00 FF FF FF FF FF FF FF FF FF FF 00 */
|
||||
if (buffer[0] == 0 && buffer[1] == 0xFF && buffer[2] == 0xFF && buffer[3] == 0xFF &&
|
||||
buffer[4] == 0xFF && buffer[5] == 0xFF && buffer[6] == 0xFF && buffer[7] == 0xFF &&
|
||||
buffer[8] == 0xFF && buffer[9] == 0xFF && buffer[10] == 0xFF && buffer[11] == 0)
|
||||
{
|
||||
/* if we didn't find a CD001 tag, this format may predate ISO-9660 */
|
||||
|
||||
/* after the 12 byte sync pattern is three bytes identifying the sector and then one byte for the mode (total 16 bytes) */
|
||||
track->stream_sector_size = 2352;
|
||||
track->stream_sector_header_size = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* attempt to determine stream_sector_size from file size */
|
||||
size_t size = intfstream_get_size(track->stream);
|
||||
|
||||
if ((size % 2352) == 0)
|
||||
{
|
||||
/* raw tracks use all 2352 bytes and have a 24 byte header */
|
||||
track->stream_sector_size = 2352;
|
||||
track->stream_sector_header_size = 24;
|
||||
}
|
||||
else if ((size % 2048) == 0)
|
||||
{
|
||||
/* cooked tracks eliminate all header/footer data */
|
||||
track->stream_sector_size = 2048;
|
||||
track->stream_sector_header_size = 0;
|
||||
}
|
||||
else if ((size % 2336) == 0)
|
||||
{
|
||||
/* MODE 2 format without 16-byte sync data */
|
||||
track->stream_sector_size = 2336;
|
||||
track->stream_sector_header_size = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cdfs_seek_track_sector(cdfs_track_t* track, unsigned int sector)
|
||||
{
|
||||
intfstream_seek(track->stream,
|
||||
sector * track->stream_sector_size +
|
||||
track->stream_sector_header_size +
|
||||
track->first_sector_offset, SEEK_SET);
|
||||
}
|
||||
|
||||
void cdfs_seek_sector(cdfs_file_t* file, unsigned int sector)
|
||||
{
|
||||
/* only allowed if open_file was called with a NULL path */
|
||||
if (file->first_sector == 0)
|
||||
{
|
||||
if (sector != file->current_sector)
|
||||
{
|
||||
file->current_sector = sector;
|
||||
file->sector_buffer_valid = 0;
|
||||
}
|
||||
|
||||
file->pos = file->current_sector * 2048;
|
||||
file->current_sector_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int cdfs_find_file(cdfs_file_t* file, const char* path)
|
||||
{
|
||||
uint8_t buffer[2048], *tmp;
|
||||
int sector, path_length;
|
||||
const char* slash = strrchr(path, '\\');
|
||||
|
||||
if (slash)
|
||||
{
|
||||
/* navigate the path to the directory record for the file */
|
||||
const int dir_length = (int)(slash - path);
|
||||
memcpy(buffer, path, dir_length);
|
||||
buffer[dir_length] = '\0';
|
||||
|
||||
sector = cdfs_find_file(file, (const char*)buffer);
|
||||
if (sector < 0)
|
||||
return sector;
|
||||
|
||||
path += dir_length + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset;
|
||||
|
||||
/* find the CD information (always 16 frames in) */
|
||||
cdfs_seek_track_sector(file->track, 16);
|
||||
intfstream_read(file->track->stream, buffer, sizeof(buffer));
|
||||
|
||||
/* the directory_record starts at 156 bytes into the sector.
|
||||
* the sector containing the root directory contents is a
|
||||
* 3 byte value that is 2 bytes into the directory_record. */
|
||||
offset = 156 + 2;
|
||||
sector = buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16);
|
||||
}
|
||||
|
||||
/* process the contents of the directory */
|
||||
cdfs_seek_track_sector(file->track, sector);
|
||||
intfstream_read(file->track->stream, buffer, sizeof(buffer));
|
||||
|
||||
path_length = strlen(path);
|
||||
tmp = buffer;
|
||||
|
||||
while (tmp < buffer + sizeof(buffer))
|
||||
{
|
||||
/* The first byte of the record is the length of
|
||||
* the record - if 0, we reached the end of the data */
|
||||
if (!*tmp)
|
||||
break;
|
||||
|
||||
/* filename is 33 bytes into the record and
|
||||
* the format is "FILENAME;version" or "DIRECTORY" */
|
||||
if ((tmp[33 + path_length] == ';' || tmp[33 + path_length] == '\0') &&
|
||||
strncasecmp((const char*)(tmp + 33), path, path_length) == 0)
|
||||
{
|
||||
/* the file size is in bytes 10-13 of the record */
|
||||
file->size = tmp[10] | (tmp[11] << 8)
|
||||
| (tmp[12] << 16) | (tmp[13] << 24);
|
||||
|
||||
/* the file contents are in the sector identified
|
||||
* in bytes 2-4 of the record */
|
||||
sector = tmp[2] | (tmp[3] << 8) | (tmp[4] << 16);
|
||||
return sector;
|
||||
}
|
||||
|
||||
/* the first byte of the record is the length of the record */
|
||||
tmp += tmp[0];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cdfs_open_file(cdfs_file_t* file, cdfs_track_t* track, const char* path)
|
||||
{
|
||||
if (!file || !track)
|
||||
return 0;
|
||||
|
||||
memset(file, 0, sizeof(*file));
|
||||
|
||||
file->track = track;
|
||||
file->current_sector = -1;
|
||||
|
||||
if (path)
|
||||
file->first_sector = cdfs_find_file(file, path);
|
||||
else if (file->track->stream_sector_size)
|
||||
{
|
||||
file->first_sector = 0;
|
||||
file->size = (intfstream_get_size(
|
||||
file->track->stream) / file->track->stream_sector_size)
|
||||
* 2048;
|
||||
}
|
||||
else
|
||||
file->first_sector = -1;
|
||||
|
||||
return (file->first_sector >= 0);
|
||||
}
|
||||
|
||||
int64_t cdfs_read_file(cdfs_file_t* file, void* buffer, uint64_t len)
|
||||
{
|
||||
int bytes_read = 0;
|
||||
|
||||
if (!file || file->first_sector < 0 || !buffer)
|
||||
return 0;
|
||||
|
||||
if (len > file->size - file->pos)
|
||||
len = file->size - file->pos;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (file->sector_buffer_valid)
|
||||
{
|
||||
size_t remaining = 2048 - file->current_sector_offset;
|
||||
if (remaining > 0)
|
||||
{
|
||||
if (remaining >= len)
|
||||
{
|
||||
memcpy(buffer,
|
||||
&file->sector_buffer[file->current_sector_offset],
|
||||
(size_t)len);
|
||||
file->current_sector_offset += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
memcpy(buffer,
|
||||
&file->sector_buffer[file->current_sector_offset], remaining);
|
||||
buffer = (char*)buffer + remaining;
|
||||
bytes_read += remaining;
|
||||
len -= remaining;
|
||||
|
||||
file->current_sector_offset += remaining;
|
||||
}
|
||||
|
||||
++file->current_sector;
|
||||
file->current_sector_offset = 0;
|
||||
file->sector_buffer_valid = 0;
|
||||
}
|
||||
else if (file->current_sector < file->first_sector)
|
||||
{
|
||||
file->current_sector = file->first_sector;
|
||||
file->current_sector_offset = 0;
|
||||
}
|
||||
|
||||
while (len >= 2048)
|
||||
{
|
||||
cdfs_seek_track_sector(file->track, file->current_sector);
|
||||
intfstream_read(file->track->stream, buffer, 2048);
|
||||
|
||||
buffer = (char*)buffer + 2048;
|
||||
bytes_read += 2048;
|
||||
|
||||
++file->current_sector;
|
||||
|
||||
len -= 2048;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
cdfs_seek_track_sector(file->track, file->current_sector);
|
||||
intfstream_read(file->track->stream, file->sector_buffer, 2048);
|
||||
memcpy(buffer, file->sector_buffer, (size_t)len);
|
||||
file->current_sector_offset = len;
|
||||
file->sector_buffer_valid = 1;
|
||||
|
||||
bytes_read += len;
|
||||
}
|
||||
|
||||
file->pos += bytes_read;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
void cdfs_close_file(cdfs_file_t* file)
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
/* not really anything to do here, just
|
||||
* clear out the first_sector so read() won't do anything */
|
||||
file->first_sector = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t cdfs_get_size(cdfs_file_t* file)
|
||||
{
|
||||
if (!file || file->first_sector < 0)
|
||||
return 0;
|
||||
|
||||
return file->size;
|
||||
}
|
||||
|
||||
int64_t cdfs_tell(cdfs_file_t* file)
|
||||
{
|
||||
if (!file || file->first_sector < 0)
|
||||
return -1;
|
||||
|
||||
return file->pos;
|
||||
}
|
||||
|
||||
int64_t cdfs_seek(cdfs_file_t* file, int64_t offset, int whence)
|
||||
{
|
||||
int64_t new_pos;
|
||||
int new_sector;
|
||||
|
||||
if (!file || file->first_sector < 0)
|
||||
return -1;
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
new_pos = offset;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
new_pos = file->pos + offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
new_pos = file->size - offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (new_pos < 0)
|
||||
return -1;
|
||||
else if (new_pos > file->size)
|
||||
return -1;
|
||||
|
||||
file->pos = (unsigned int)new_pos;
|
||||
file->current_sector_offset = file->pos % 2048;
|
||||
|
||||
new_sector = file->pos / 2048;
|
||||
if (new_sector != file->current_sector)
|
||||
{
|
||||
file->current_sector = new_sector;
|
||||
file->sector_buffer_valid = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdfs_skip_spaces(const char** ptr)
|
||||
{
|
||||
while (**ptr && (**ptr == ' ' || **ptr == '\t'))
|
||||
++(*ptr);
|
||||
}
|
||||
|
||||
static cdfs_track_t* cdfs_wrap_stream(
|
||||
intfstream_t* stream, unsigned first_sector_offset)
|
||||
{
|
||||
cdfs_track_t* track = NULL;
|
||||
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
track = (cdfs_track_t*)
|
||||
calloc(1, sizeof(*track));
|
||||
track->stream = stream;
|
||||
track->first_sector_offset = first_sector_offset;
|
||||
|
||||
cdfs_determine_sector_size(track);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
static cdfs_track_t* cdfs_open_cue_track(
|
||||
const char* path, unsigned int track_index)
|
||||
{
|
||||
char* cue = NULL;
|
||||
const char* line = NULL;
|
||||
int found_track = 0;
|
||||
char current_track_path[PATH_MAX_LENGTH] = {0};
|
||||
char track_path[PATH_MAX_LENGTH] = {0};
|
||||
unsigned int sector_size = 0;
|
||||
unsigned int previous_sector_size = 0;
|
||||
unsigned int previous_index_sector_offset= 0;
|
||||
unsigned int track_offset = 0;
|
||||
intfstream_t *cue_stream = intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
int64_t stream_size = intfstream_get_size(cue_stream);
|
||||
char *cue_contents = (char*)malloc((size_t)(stream_size + 1));
|
||||
cdfs_track_t* track = NULL;
|
||||
|
||||
if (!cue_contents)
|
||||
{
|
||||
intfstream_close(cue_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
intfstream_read(cue_stream, cue_contents, stream_size);
|
||||
intfstream_close(cue_stream);
|
||||
|
||||
cue_contents[stream_size] = '\0';
|
||||
|
||||
cue = cue_contents;
|
||||
while (*cue)
|
||||
{
|
||||
cdfs_skip_spaces((const char**)&cue);
|
||||
line = cue;
|
||||
|
||||
while (*cue && *cue != '\n')
|
||||
++cue;
|
||||
if (*cue)
|
||||
*cue++ = '\0';
|
||||
|
||||
if (!strncasecmp(line, "FILE", 4))
|
||||
{
|
||||
const char *file = line + 4;
|
||||
cdfs_skip_spaces(&file);
|
||||
|
||||
if (file[0])
|
||||
{
|
||||
const char *file_end = cue - 1;
|
||||
while (file_end > file && *file_end != ' ' && *file_end != '\t')
|
||||
--file_end;
|
||||
|
||||
if (file[0] == '"' && file_end[-1] == '"')
|
||||
{
|
||||
++file;
|
||||
--file_end;
|
||||
}
|
||||
|
||||
memcpy(current_track_path, file, file_end - file);
|
||||
current_track_path[file_end - file] = '\0';
|
||||
}
|
||||
|
||||
previous_sector_size = 0;
|
||||
previous_index_sector_offset = 0;
|
||||
track_offset = 0;
|
||||
}
|
||||
else if (!strncasecmp(line, "TRACK", 5))
|
||||
{
|
||||
unsigned track_number = 0;
|
||||
|
||||
const char *track = line + 5;
|
||||
cdfs_skip_spaces(&track);
|
||||
|
||||
sscanf(track, "%d", (int*)&track_number);
|
||||
while (*track && *track != ' ' && *track != '\n')
|
||||
++track;
|
||||
|
||||
previous_sector_size = sector_size;
|
||||
|
||||
cdfs_skip_spaces(&track);
|
||||
|
||||
if (!strncasecmp(track, "MODE", 4))
|
||||
{
|
||||
/* track_index = 0 means find the first data track */
|
||||
if (!track_index || track_index == track_number)
|
||||
found_track = track_number;
|
||||
|
||||
sector_size = atoi(track + 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* assume AUDIO */
|
||||
sector_size = 2352;
|
||||
}
|
||||
}
|
||||
else if (!strncasecmp(line, "INDEX", 5))
|
||||
{
|
||||
unsigned min = 0, sec = 0, frame = 0;
|
||||
unsigned index_number = 0;
|
||||
unsigned sector_offset;
|
||||
const char *index = line + 5;
|
||||
|
||||
cdfs_skip_spaces(&index);
|
||||
sscanf(index, "%u", &index_number);
|
||||
while (*index && *index != ' ' && *index != '\n')
|
||||
++index;
|
||||
cdfs_skip_spaces(&index);
|
||||
|
||||
sscanf(index, "%u:%u:%u", &min, &sec, &frame);
|
||||
sector_offset = ((min * 60) + sec) * 75 + frame;
|
||||
sector_offset -= previous_index_sector_offset;
|
||||
track_offset += sector_offset * previous_sector_size;
|
||||
previous_sector_size = sector_size;
|
||||
previous_index_sector_offset += sector_offset;
|
||||
|
||||
if (found_track && index_number == 1)
|
||||
{
|
||||
if ( strstr(current_track_path, "/") ||
|
||||
strstr(current_track_path, "\\"))
|
||||
strncpy(track_path, current_track_path, sizeof(track_path));
|
||||
else
|
||||
{
|
||||
fill_pathname_basedir(track_path, path, sizeof(track_path));
|
||||
strlcat(track_path, current_track_path, sizeof(track_path));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(cue_contents);
|
||||
|
||||
if (string_is_empty(track_path))
|
||||
return NULL;
|
||||
|
||||
track = cdfs_wrap_stream(intfstream_open_file(
|
||||
track_path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE), track_offset);
|
||||
|
||||
if (track && track->stream_sector_size == 0)
|
||||
{
|
||||
track->stream_sector_size = sector_size;
|
||||
|
||||
if (sector_size == 2352)
|
||||
track->stream_sector_header_size = 16;
|
||||
else if (sector_size == 2336)
|
||||
track->stream_sector_header_size = 8;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
static cdfs_track_t* cdfs_open_chd_track(const char* path, int32_t track_index)
|
||||
{
|
||||
intfstream_t* intf_stream;
|
||||
cdfs_track_t* track;
|
||||
|
||||
intf_stream = intfstream_open_chd_track(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE,
|
||||
track_index);
|
||||
if (!intf_stream)
|
||||
return NULL;
|
||||
|
||||
track = cdfs_wrap_stream(intf_stream,
|
||||
intfstream_get_offset_to_start(intf_stream));
|
||||
|
||||
if (track && track->stream_sector_header_size == 0)
|
||||
{
|
||||
track->stream_sector_size = intfstream_get_frame_size(intf_stream);
|
||||
|
||||
if (track->stream_sector_size == 2352)
|
||||
track->stream_sector_header_size = 16;
|
||||
else if (track->stream_sector_size == 2336)
|
||||
track->stream_sector_header_size = 8;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct cdfs_track_t* cdfs_open_track(const char* path,
|
||||
unsigned int track_index)
|
||||
{
|
||||
const char* ext = path_get_extension(path);
|
||||
|
||||
if (string_is_equal_noncase(ext, "cue"))
|
||||
return cdfs_open_cue_track(path, track_index);
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(ext, "chd"))
|
||||
return cdfs_open_chd_track(path, track_index);
|
||||
#endif
|
||||
|
||||
/* if opening track 1, try opening as a raw track */
|
||||
if (track_index == 1)
|
||||
return cdfs_open_raw_track(path);
|
||||
|
||||
/* unsupported file type */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct cdfs_track_t* cdfs_open_data_track(const char* path)
|
||||
{
|
||||
const char* ext = path_get_extension(path);
|
||||
|
||||
if (string_is_equal_noncase(ext, "cue"))
|
||||
return cdfs_open_cue_track(path, 0);
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(ext, "chd"))
|
||||
return cdfs_open_chd_track(path, CHDSTREAM_TRACK_PRIMARY);
|
||||
#endif
|
||||
|
||||
/* unsupported file type - try opening as a raw track */
|
||||
return cdfs_open_raw_track(path);
|
||||
}
|
||||
|
||||
cdfs_track_t* cdfs_open_raw_track(const char* path)
|
||||
{
|
||||
const char* ext = path_get_extension(path);
|
||||
cdfs_track_t* track = NULL;
|
||||
|
||||
if ( string_is_equal_noncase(ext, "bin") ||
|
||||
string_is_equal_noncase(ext, "iso"))
|
||||
{
|
||||
intfstream_t* file = intfstream_open_file(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
track = cdfs_wrap_stream(file, 0);
|
||||
if (track != NULL && track->stream_sector_size == 0)
|
||||
{
|
||||
cdfs_close_track(track);
|
||||
track = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* unsupported file type */
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
void cdfs_close_track(cdfs_track_t* track)
|
||||
{
|
||||
if (track)
|
||||
{
|
||||
if (track->stream)
|
||||
{
|
||||
intfstream_close(track->stream);
|
||||
free(track->stream);
|
||||
}
|
||||
|
||||
free(track);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (image_texture.c).
|
||||
@ -28,26 +28,51 @@
|
||||
#include <boolean.h>
|
||||
#include <formats/image.h>
|
||||
#include <file/nbio.h>
|
||||
|
||||
#include <string/stdstring.h>
|
||||
|
||||
enum image_type_enum image_texture_get_type(const char *path)
|
||||
{
|
||||
#ifdef HAVE_RTGA
|
||||
if (strstr(path, ".tga"))
|
||||
return IMAGE_TYPE_TGA;
|
||||
#endif
|
||||
/* We are comparing against a fixed list of file
|
||||
* extensions, the longest (jpeg) being 4 characters
|
||||
* in length. We therefore only need to extract the first
|
||||
* 5 characters from the extension of the input path
|
||||
* to correctly validate a match */
|
||||
const char *ext = NULL;
|
||||
char ext_lower[6];
|
||||
|
||||
ext_lower[0] = '\0';
|
||||
|
||||
if (string_is_empty(path))
|
||||
return IMAGE_TYPE_NONE;
|
||||
|
||||
/* Get file extension */
|
||||
ext = strrchr(path, '.');
|
||||
|
||||
if (!ext || (*(++ext) == '\0'))
|
||||
return IMAGE_TYPE_NONE;
|
||||
|
||||
/* Copy and convert to lower case */
|
||||
strlcpy(ext_lower, ext, sizeof(ext_lower));
|
||||
string_to_lower(ext_lower);
|
||||
|
||||
#ifdef HAVE_RPNG
|
||||
if (strstr(path, ".png"))
|
||||
if (string_is_equal(ext_lower, "png"))
|
||||
return IMAGE_TYPE_PNG;
|
||||
#endif
|
||||
#ifdef HAVE_RJPEG
|
||||
if (strstr(path, ".jpg") || strstr(path, ".jpeg"))
|
||||
if (string_is_equal(ext_lower, "jpg") ||
|
||||
string_is_equal(ext_lower, "jpeg"))
|
||||
return IMAGE_TYPE_JPEG;
|
||||
#endif
|
||||
#ifdef HAVE_RBMP
|
||||
if (strstr(path, ".bmp"))
|
||||
if (string_is_equal(ext_lower, "bmp"))
|
||||
return IMAGE_TYPE_BMP;
|
||||
#endif
|
||||
#ifdef HAVE_RTGA
|
||||
if (string_is_equal(ext_lower, "tga"))
|
||||
return IMAGE_TYPE_TGA;
|
||||
#endif
|
||||
|
||||
return IMAGE_TYPE_NONE;
|
||||
}
|
||||
|
||||
@ -90,8 +115,13 @@ bool image_texture_color_convert(unsigned r_shift,
|
||||
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);
|
||||
/* Explicitly cast these to uint32_t to prevent
|
||||
* ASAN runtime error: left shift of 255 by 24 places
|
||||
* cannot be represented in type 'int' */
|
||||
pixels[i] = ((uint32_t)a << a_shift) |
|
||||
((uint32_t)r << r_shift) |
|
||||
((uint32_t)g << g_shift) |
|
||||
((uint32_t)b << b_shift);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -190,7 +220,7 @@ static bool image_texture_load_internal(
|
||||
ret = image_transfer_process(img, type,
|
||||
(uint32_t**)&out_img->pixels, len, &out_img->width,
|
||||
&out_img->height);
|
||||
}while(ret == IMAGE_PROCESS_NEXT);
|
||||
} while (ret == IMAGE_PROCESS_NEXT);
|
||||
|
||||
if (ret == IMAGE_PROCESS_ERROR || ret == IMAGE_PROCESS_ERROR_END)
|
||||
goto end;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (image_transfer.c).
|
||||
|
73
externals/libretro-common/formats/jpeg/rjpeg.c
vendored
73
externals/libretro-common/formats/jpeg/rjpeg.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rjpeg.c).
|
||||
@ -141,17 +141,15 @@ struct rjpeg
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t img_x;
|
||||
uint32_t img_y;
|
||||
int img_n;
|
||||
int img_out_n;
|
||||
|
||||
int buflen;
|
||||
uint8_t buffer_start[128];
|
||||
|
||||
uint8_t *img_buffer;
|
||||
uint8_t *img_buffer_end;
|
||||
uint8_t *img_buffer_original;
|
||||
int img_n;
|
||||
int img_out_n;
|
||||
int buflen;
|
||||
uint32_t img_x;
|
||||
uint32_t img_y;
|
||||
uint8_t buffer_start[128];
|
||||
} rjpeg_context;
|
||||
|
||||
static INLINE uint8_t rjpeg_get8(rjpeg_context *s)
|
||||
@ -171,31 +169,32 @@ static INLINE uint8_t rjpeg_get8(rjpeg_context *s)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t fast[1 << FAST_BITS];
|
||||
/* weirdly, repacking this into AoS is a 10% speed loss, instead of a win */
|
||||
uint16_t code[256];
|
||||
uint8_t values[256];
|
||||
uint8_t size[257];
|
||||
unsigned int maxcode[18];
|
||||
int delta[17]; /* old 'firstsymbol' - old 'firstcode' */
|
||||
/* weirdly, repacking this into AoS is a 10% speed loss, instead of a win */
|
||||
uint16_t code[256];
|
||||
uint8_t fast[1 << FAST_BITS];
|
||||
uint8_t values[256];
|
||||
uint8_t size[257];
|
||||
} rjpeg_huffman;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
rjpeg_context *s;
|
||||
rjpeg_huffman huff_dc[4];
|
||||
rjpeg_huffman huff_ac[4];
|
||||
uint8_t dequant[4][64];
|
||||
int16_t fast_ac[4][1 << FAST_BITS];
|
||||
|
||||
/* sizes for components, interleaved MCUs */
|
||||
int img_h_max, img_v_max;
|
||||
int img_mcu_x, img_mcu_y;
|
||||
int img_mcu_w, img_mcu_h;
|
||||
/* kernels */
|
||||
void (*idct_block_kernel)(uint8_t *out, int out_stride, short data[64]);
|
||||
void (*YCbCr_to_RGB_kernel)(uint8_t *out, const uint8_t *y, const uint8_t *pcb,
|
||||
const uint8_t *pcr, int count, int step);
|
||||
uint8_t *(*resample_row_hv_2_kernel)(uint8_t *out, uint8_t *in_near,
|
||||
uint8_t *in_far, int w, int hs);
|
||||
|
||||
/* definition of jpeg image component */
|
||||
struct
|
||||
{
|
||||
uint8_t *data;
|
||||
void *raw_data, *raw_coeff;
|
||||
uint8_t *linebuf;
|
||||
short *coeff; /* progressive only */
|
||||
int id;
|
||||
int h,v;
|
||||
int tq;
|
||||
@ -203,35 +202,31 @@ typedef struct
|
||||
int dc_pred;
|
||||
|
||||
int x,y,w2,h2;
|
||||
uint8_t *data;
|
||||
void *raw_data, *raw_coeff;
|
||||
uint8_t *linebuf;
|
||||
short *coeff; /* progressive only */
|
||||
int coeff_w; /* number of 8x8 coefficient blocks */
|
||||
int coeff_h; /* number of 8x8 coefficient blocks */
|
||||
} img_comp[4];
|
||||
|
||||
uint32_t code_buffer; /* jpeg entropy-coded buffer */
|
||||
int code_bits; /* number of valid bits */
|
||||
unsigned char marker; /* marker seen while filling entropy buffer */
|
||||
int nomore; /* flag if we saw a marker so must stop */
|
||||
/* sizes for components, interleaved MCUs */
|
||||
int img_h_max, img_v_max;
|
||||
int img_mcu_x, img_mcu_y;
|
||||
int img_mcu_w, img_mcu_h;
|
||||
|
||||
int code_bits; /* number of valid bits */
|
||||
int nomore; /* flag if we saw a marker so must stop */
|
||||
int progressive;
|
||||
int spec_start;
|
||||
int spec_end;
|
||||
int succ_high;
|
||||
int succ_low;
|
||||
int eob_run;
|
||||
|
||||
int scan_n, order[4];
|
||||
int restart_interval, todo;
|
||||
|
||||
/* kernels */
|
||||
void (*idct_block_kernel)(uint8_t *out, int out_stride, short data[64]);
|
||||
void (*YCbCr_to_RGB_kernel)(uint8_t *out, const uint8_t *y, const uint8_t *pcb,
|
||||
const uint8_t *pcr, int count, int step);
|
||||
uint8_t *(*resample_row_hv_2_kernel)(uint8_t *out, uint8_t *in_near,
|
||||
uint8_t *in_far, int w, int hs);
|
||||
uint32_t code_buffer; /* jpeg entropy-coded buffer */
|
||||
rjpeg_huffman huff_dc[4]; /* unsigned int alignment */
|
||||
rjpeg_huffman huff_ac[4]; /* unsigned int alignment */
|
||||
int16_t fast_ac[4][1 << FAST_BITS];
|
||||
unsigned char marker; /* marker seen while filling entropy buffer */
|
||||
uint8_t dequant[4][64];
|
||||
} rjpeg_jpeg;
|
||||
|
||||
#define RJPEG_F2F(x) ((int) (((x) * 4096 + 0.5)))
|
||||
|
316
externals/libretro-common/formats/json/jsonsax.c
vendored
316
externals/libretro-common/formats/json/jsonsax.c
vendored
@ -1,316 +0,0 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (jsonsax.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <formats/jsonsax.h>
|
||||
|
||||
#ifdef JSONSAX_ERRORS
|
||||
const char* jsonsax_errors[] =
|
||||
{
|
||||
"Ok",
|
||||
"Interrupted",
|
||||
"Missing key",
|
||||
"Unterminated key",
|
||||
"Missing value",
|
||||
"Unterminated object",
|
||||
"Unterminated array",
|
||||
"Unterminated string",
|
||||
"Invalid value"
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const jsonsax_handlers_t* handlers;
|
||||
|
||||
const char* json;
|
||||
void* ud;
|
||||
jmp_buf env;
|
||||
}
|
||||
state_t;
|
||||
|
||||
static INLINE void skip_spaces( state_t* state )
|
||||
{
|
||||
while ( isspace( (unsigned char)*state->json ) )
|
||||
state->json++;
|
||||
}
|
||||
|
||||
static INLINE void skip_digits( state_t* state )
|
||||
{
|
||||
while ( isdigit( (unsigned char)*state->json ) )
|
||||
state->json++;
|
||||
}
|
||||
|
||||
#define HANDLE_0( event ) \
|
||||
do { \
|
||||
if ( state->handlers->event && state->handlers->event( state->ud ) ) \
|
||||
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
||||
} while ( 0 )
|
||||
|
||||
#define HANDLE_1( event, arg1 ) \
|
||||
do { \
|
||||
if ( state->handlers->event && state->handlers->event( state->ud, arg1 ) ) \
|
||||
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
||||
} while ( 0 )
|
||||
|
||||
#define HANDLE_2( event, arg1, arg2 ) \
|
||||
do { \
|
||||
if ( state->handlers->event && state->handlers->event( state->ud, arg1, arg2 ) ) \
|
||||
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
||||
} while ( 0 )
|
||||
|
||||
static void jsonx_parse_value(state_t* state);
|
||||
|
||||
static void jsonx_parse_object( state_t* state )
|
||||
{
|
||||
state->json++; /* we're sure the current character is a '{' */
|
||||
skip_spaces( state );
|
||||
HANDLE_0( start_object );
|
||||
|
||||
while ( *state->json != '}' )
|
||||
{
|
||||
const char *name = NULL;
|
||||
if ( *state->json != '"' )
|
||||
longjmp( state->env, JSONSAX_MISSING_KEY );
|
||||
|
||||
name = ++state->json;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
const char* quote = strchr( state->json, '"' );
|
||||
|
||||
if ( !quote )
|
||||
longjmp( state->env, JSONSAX_UNTERMINATED_KEY );
|
||||
|
||||
state->json = quote + 1;
|
||||
|
||||
if ( quote[ -1 ] != '\\' )
|
||||
break;
|
||||
}
|
||||
|
||||
HANDLE_2( key, name, state->json - name - 1 );
|
||||
skip_spaces( state );
|
||||
|
||||
if ( *state->json != ':' )
|
||||
longjmp( state->env, JSONSAX_MISSING_VALUE );
|
||||
|
||||
state->json++;
|
||||
skip_spaces( state );
|
||||
jsonx_parse_value( state );
|
||||
skip_spaces( state );
|
||||
|
||||
if ( *state->json != ',' )
|
||||
break;
|
||||
|
||||
state->json++;
|
||||
skip_spaces( state );
|
||||
}
|
||||
|
||||
if ( *state->json != '}' )
|
||||
longjmp( state->env, JSONSAX_UNTERMINATED_OBJECT );
|
||||
|
||||
state->json++;
|
||||
HANDLE_0( end_object );
|
||||
}
|
||||
|
||||
static void jsonx_parse_array(state_t* state)
|
||||
{
|
||||
unsigned int ndx = 0;
|
||||
|
||||
state->json++; /* we're sure the current character is a '[' */
|
||||
skip_spaces( state );
|
||||
HANDLE_0( start_array );
|
||||
|
||||
while ( *state->json != ']' )
|
||||
{
|
||||
HANDLE_1( array_index, ndx++ );
|
||||
jsonx_parse_value( state );
|
||||
skip_spaces( state );
|
||||
|
||||
if ( *state->json != ',' )
|
||||
break;
|
||||
|
||||
state->json++;
|
||||
skip_spaces( state );
|
||||
}
|
||||
|
||||
if ( *state->json != ']' )
|
||||
longjmp( state->env, JSONSAX_UNTERMINATED_ARRAY );
|
||||
|
||||
state->json++;
|
||||
HANDLE_0( end_array );
|
||||
}
|
||||
|
||||
static void jsonx_parse_string(state_t* state)
|
||||
{
|
||||
const char* string = ++state->json;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
const char* quote = strchr( state->json, '"' );
|
||||
|
||||
if ( !quote )
|
||||
longjmp( state->env, JSONSAX_UNTERMINATED_STRING );
|
||||
|
||||
state->json = quote + 1;
|
||||
|
||||
if ( quote[ -1 ] != '\\' )
|
||||
break;
|
||||
}
|
||||
|
||||
HANDLE_2( string, string, state->json - string - 1 );
|
||||
}
|
||||
|
||||
static void jsonx_parse_boolean(state_t* state)
|
||||
{
|
||||
if ( !strncmp( state->json, "true", 4 ) )
|
||||
{
|
||||
state->json += 4;
|
||||
HANDLE_1( boolean, 1 );
|
||||
}
|
||||
else if ( !strncmp( state->json, "false", 5 ) )
|
||||
{
|
||||
state->json += 5;
|
||||
HANDLE_1( boolean, 0 );
|
||||
}
|
||||
else
|
||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||
}
|
||||
|
||||
static void jsonx_parse_null(state_t* state)
|
||||
{
|
||||
if ( !strncmp( state->json + 1, "ull", 3 ) ) /* we're sure the current character is a 'n' */
|
||||
{
|
||||
state->json += 4;
|
||||
HANDLE_0( null );
|
||||
}
|
||||
else
|
||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||
}
|
||||
|
||||
static void jsonx_parse_number(state_t* state)
|
||||
{
|
||||
const char* number = state->json;
|
||||
|
||||
if ( *state->json == '-' )
|
||||
state->json++;
|
||||
|
||||
if ( !isdigit( (unsigned char)*state->json ) )
|
||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||
|
||||
skip_digits( state );
|
||||
|
||||
if ( *state->json == '.' )
|
||||
{
|
||||
state->json++;
|
||||
|
||||
if ( !isdigit( (unsigned char)*state->json ) )
|
||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||
|
||||
skip_digits( state );
|
||||
}
|
||||
|
||||
if ( *state->json == 'e' || *state->json == 'E' )
|
||||
{
|
||||
state->json++;
|
||||
|
||||
if ( *state->json == '-' || *state->json == '+' )
|
||||
state->json++;
|
||||
|
||||
if ( !isdigit( (unsigned char)*state->json ) )
|
||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||
|
||||
skip_digits( state );
|
||||
}
|
||||
|
||||
HANDLE_2( number, number, state->json - number );
|
||||
}
|
||||
|
||||
static void jsonx_parse_value(state_t* state)
|
||||
{
|
||||
skip_spaces( state );
|
||||
|
||||
switch ( *state->json )
|
||||
{
|
||||
case '{':
|
||||
jsonx_parse_object(state);
|
||||
break;
|
||||
case '[':
|
||||
jsonx_parse_array( state );
|
||||
break;
|
||||
case '"':
|
||||
jsonx_parse_string( state );
|
||||
break;
|
||||
case 't':
|
||||
case 'f':
|
||||
jsonx_parse_boolean( state );
|
||||
break;
|
||||
case 'n':
|
||||
jsonx_parse_null( state );
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
jsonx_parse_number( state );
|
||||
break;
|
||||
|
||||
default:
|
||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
||||
}
|
||||
}
|
||||
|
||||
int jsonsax_parse( const char* json, const jsonsax_handlers_t* handlers, void* userdata )
|
||||
{
|
||||
state_t state;
|
||||
int res;
|
||||
|
||||
state.json = json;
|
||||
state.handlers = handlers;
|
||||
state.ud = userdata;
|
||||
|
||||
if ( ( res = setjmp( state.env ) ) == 0 )
|
||||
{
|
||||
if ( handlers->start_document )
|
||||
handlers->start_document( userdata );
|
||||
|
||||
jsonx_parse_value(&state);
|
||||
|
||||
if ( handlers->end_document )
|
||||
handlers->end_document( userdata );
|
||||
|
||||
res = JSONSAX_OK;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
3929
externals/libretro-common/formats/json/jsonsax_full.c
vendored
3929
externals/libretro-common/formats/json/jsonsax_full.c
vendored
File diff suppressed because it is too large
Load Diff
1446
externals/libretro-common/formats/json/rjson.c
vendored
Normal file
1446
externals/libretro-common/formats/json/rjson.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -65,8 +65,6 @@
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
|
||||
|
||||
/***************************************************************************
|
||||
DEBUGGING
|
||||
***************************************************************************/
|
||||
@ -95,18 +93,22 @@
|
||||
|
||||
#define NO_MATCH (~0)
|
||||
|
||||
#define MAP_ENTRY_TYPE_INVALID 0x0000 /* invalid type */
|
||||
#define MAP_ENTRY_TYPE_COMPRESSED 0x0001 /* standard compression */
|
||||
#define MAP_ENTRY_TYPE_UNCOMPRESSED 0x0002 /* uncompressed data */
|
||||
#define MAP_ENTRY_TYPE_MINI 0x0003 /* mini: use offset as raw data */
|
||||
#define MAP_ENTRY_TYPE_SELF_HUNK 0x0004 /* same as another hunk in this file */
|
||||
#define MAP_ENTRY_TYPE_PARENT_HUNK 0x0005 /* same as a hunk in the parent file */
|
||||
#define MAP_ENTRY_TYPE_2ND_COMPRESSED 0x0006 /* compressed with secondary algorithm (usually FLAC CDDA) */
|
||||
|
||||
#ifdef WANT_RAW_DATA_SECTOR
|
||||
const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 };
|
||||
#endif
|
||||
|
||||
/* V3-V4 entry types */
|
||||
enum
|
||||
{
|
||||
V34_MAP_ENTRY_TYPE_INVALID = 0, /* invalid type */
|
||||
V34_MAP_ENTRY_TYPE_COMPRESSED = 1, /* standard compression */
|
||||
V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, /* uncompressed data */
|
||||
V34_MAP_ENTRY_TYPE_MINI = 3, /* mini: use offset as raw data */
|
||||
V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, /* same as another hunk in this file */
|
||||
V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, /* same as a hunk in the parent file */
|
||||
V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 /* compressed with secondary algorithm (usually FLAC CDDA) */
|
||||
};
|
||||
|
||||
/* V5 compression types */
|
||||
enum
|
||||
{
|
||||
@ -147,8 +149,6 @@ enum
|
||||
MACROS
|
||||
***************************************************************************/
|
||||
|
||||
#define SET_ERROR_AND_CLEANUP(err) do { last_error = (err); goto cleanup; } while (0)
|
||||
|
||||
#define EARLY_EXIT(x) do { (void)(x); goto cleanup; } while (0)
|
||||
|
||||
/***************************************************************************
|
||||
@ -262,13 +262,6 @@ static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metai
|
||||
CODEC INTERFACES
|
||||
***************************************************************************/
|
||||
|
||||
#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
|
||||
|
||||
/* general codecs with CD frontend */
|
||||
#define CHD_CODEC_CD_ZLIB CHD_MAKE_TAG('c','d','z','l')
|
||||
#define CHD_CODEC_CD_LZMA CHD_MAKE_TAG('c','d','l','z')
|
||||
#define CHD_CODEC_CD_FLAC CHD_MAKE_TAG('c','d','f','l')
|
||||
|
||||
static const codec_interface codec_interfaces[] =
|
||||
{
|
||||
/* "none" or no compression */
|
||||
@ -305,6 +298,17 @@ static const codec_interface codec_interfaces[] =
|
||||
NULL
|
||||
},
|
||||
|
||||
/* V5 zlib compression */
|
||||
{
|
||||
CHD_CODEC_ZLIB,
|
||||
"zlib (Deflate)",
|
||||
FALSE,
|
||||
zlib_codec_init,
|
||||
zlib_codec_free,
|
||||
zlib_codec_decompress,
|
||||
NULL
|
||||
},
|
||||
|
||||
/* V5 CD zlib compression */
|
||||
{
|
||||
CHD_CODEC_CD_ZLIB,
|
||||
@ -417,12 +421,12 @@ static INLINE UINT32 get_bigendian_uint32(const UINT8 *base)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static INLINE void put_bigendian_uint24(UINT8 *base, UINT32 value)
|
||||
static INLINE void put_bigendian_uint32(UINT8 *base, UINT32 value)
|
||||
{
|
||||
value &= 0xffffff;
|
||||
base[0] = value >> 16;
|
||||
base[1] = value >> 8;
|
||||
base[2] = value;
|
||||
base[0] = value >> 24;
|
||||
base[1] = value >> 16;
|
||||
base[2] = value >> 8;
|
||||
base[3] = value;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
@ -430,7 +434,7 @@ static INLINE void put_bigendian_uint24(UINT8 *base, UINT32 value)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static INLINE void put_bigendian_uint32(UINT8 *base, UINT32 value)
|
||||
static INLINE void put_bigendian_uint24(UINT8 *base, UINT32 value)
|
||||
{
|
||||
value &= 0xffffff;
|
||||
base[0] = value >> 16;
|
||||
@ -745,7 +749,7 @@ static INLINE void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 h
|
||||
entry->offset = get_bigendian_uint64(&base[0]);
|
||||
entry->crc = 0;
|
||||
entry->length = entry->offset >> 44;
|
||||
entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? MAP_ENTRY_TYPE_UNCOMPRESSED : MAP_ENTRY_TYPE_COMPRESSED);
|
||||
entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? V34_MAP_ENTRY_TYPE_UNCOMPRESSED : V34_MAP_ENTRY_TYPE_COMPRESSED);
|
||||
#ifdef __MWERKS__
|
||||
entry->offset = entry->offset & 0x00000FFFFFFFFFFFLL;
|
||||
#else
|
||||
@ -826,14 +830,13 @@ chd_error chd_open_file(RFILE *file, int mode, chd_file *parent, chd_file **chd)
|
||||
if (newchd->header.version < 5)
|
||||
{
|
||||
err = map_read(newchd);
|
||||
if (err != CHDERR_NONE)
|
||||
EARLY_EXIT(err);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = decompress_v5_map(newchd, &(newchd->header));
|
||||
(void)err;
|
||||
}
|
||||
if (err != CHDERR_NONE)
|
||||
EARLY_EXIT(err);
|
||||
|
||||
#ifdef NEED_CACHE_HUNK
|
||||
/* allocate and init the hunk cache */
|
||||
@ -894,6 +897,12 @@ chd_error chd_open_file(RFILE *file, int mode, chd_file *parent, chd_file **chd)
|
||||
void* codec = NULL;
|
||||
switch (newchd->header.compression[decompnum])
|
||||
{
|
||||
case CHD_CODEC_ZLIB:
|
||||
#ifdef HAVE_ZLIB
|
||||
codec = &newchd->zlib_codec_data;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case CHD_CODEC_CD_ZLIB:
|
||||
#ifdef HAVE_ZLIB
|
||||
codec = &newchd->cdzl_codec_data;
|
||||
@ -940,6 +949,38 @@ cleanup:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
chd_precache - precache underlying file in
|
||||
memory
|
||||
-------------------------------------------------*/
|
||||
|
||||
chd_error chd_precache(chd_file *chd)
|
||||
{
|
||||
int64_t size, count;
|
||||
|
||||
if (!chd->file_cache)
|
||||
{
|
||||
filestream_seek(chd->file, 0, SEEK_END);
|
||||
size = filestream_tell(chd->file);
|
||||
if (size <= 0)
|
||||
return CHDERR_INVALID_DATA;
|
||||
chd->file_cache = (UINT8*)malloc(size);
|
||||
if (chd->file_cache == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
filestream_seek(chd->file, 0, SEEK_SET);
|
||||
count = filestream_read(chd->file, chd->file_cache, size);
|
||||
if (count != size)
|
||||
{
|
||||
free(chd->file_cache);
|
||||
chd->file_cache = NULL;
|
||||
return CHDERR_READ_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
chd_open - open a CHD file by
|
||||
filename
|
||||
@ -986,32 +1027,6 @@ cleanup:
|
||||
return err;
|
||||
}
|
||||
|
||||
chd_error chd_precache(chd_file *chd)
|
||||
{
|
||||
int64_t size, count;
|
||||
|
||||
if (!chd->file_cache)
|
||||
{
|
||||
filestream_seek(chd->file, 0, SEEK_END);
|
||||
size = filestream_tell(chd->file);
|
||||
if (size <= 0)
|
||||
return CHDERR_INVALID_DATA;
|
||||
chd->file_cache = (UINT8*)malloc(size);
|
||||
if (chd->file_cache == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
filestream_seek(chd->file, 0, SEEK_SET);
|
||||
count = filestream_read(chd->file, chd->file_cache, size);
|
||||
if (count != size)
|
||||
{
|
||||
free(chd->file_cache);
|
||||
chd->file_cache = NULL;
|
||||
return CHDERR_READ_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
chd_close - close a CHD file for access
|
||||
-------------------------------------------------*/
|
||||
@ -1048,6 +1063,12 @@ void chd_close(chd_file *chd)
|
||||
#endif
|
||||
break;
|
||||
|
||||
case CHD_CODEC_ZLIB:
|
||||
#ifdef HAVE_ZLIB
|
||||
codec = &chd->zlib_codec_data;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case CHD_CODEC_CD_ZLIB:
|
||||
#ifdef HAVE_ZLIB
|
||||
codec = &chd->cdzl_codec_data;
|
||||
@ -1357,6 +1378,7 @@ static UINT32 header_guess_unitbytes(chd_file *chd)
|
||||
if (chd_get_metadata(chd, CDROM_OLD_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE ||
|
||||
chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE ||
|
||||
chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE ||
|
||||
chd_get_metadata(chd, GDROM_OLD_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE ||
|
||||
chd_get_metadata(chd, GDROM_TRACK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE)
|
||||
return CD_FRAME_SIZE;
|
||||
|
||||
@ -1531,7 +1553,12 @@ static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum)
|
||||
}
|
||||
#endif
|
||||
|
||||
static UINT8* read_compressed(chd_file *chd, UINT64 offset, size_t size)
|
||||
/*-------------------------------------------------
|
||||
hunk_read_compressed - read a compressed
|
||||
hunk
|
||||
-------------------------------------------------*/
|
||||
|
||||
static UINT8* hunk_read_compressed(chd_file *chd, UINT64 offset, size_t size)
|
||||
{
|
||||
int64_t bytes;
|
||||
if (chd->file_cache)
|
||||
@ -1543,7 +1570,12 @@ static UINT8* read_compressed(chd_file *chd, UINT64 offset, size_t size)
|
||||
return chd->compressed;
|
||||
}
|
||||
|
||||
static chd_error read_uncompressed(chd_file *chd, UINT64 offset, size_t size, UINT8 *dest)
|
||||
/*-------------------------------------------------
|
||||
hunk_read_uncompressed - read an uncompressed
|
||||
hunk
|
||||
-------------------------------------------------*/
|
||||
|
||||
static chd_error hunk_read_uncompressed(chd_file *chd, UINT64 offset, size_t size, UINT8 *dest)
|
||||
{
|
||||
int64_t bytes;
|
||||
if (chd->file_cache)
|
||||
@ -1582,17 +1614,20 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
{
|
||||
map_entry *entry = &chd->map[hunknum];
|
||||
UINT32 bytes;
|
||||
UINT8* compressed_bytes;
|
||||
|
||||
/* switch off the entry type */
|
||||
switch (entry->flags & MAP_ENTRY_FLAG_TYPE_MASK)
|
||||
{
|
||||
/* compressed data */
|
||||
case MAP_ENTRY_TYPE_COMPRESSED:
|
||||
case V34_MAP_ENTRY_TYPE_COMPRESSED:
|
||||
{
|
||||
/* read it into the decompression buffer */
|
||||
|
||||
void *codec;
|
||||
UINT8 *bytes = read_compressed(chd, entry->offset,
|
||||
compressed_bytes = hunk_read_compressed(chd, entry->offset,
|
||||
entry->length);
|
||||
if (bytes == NULL)
|
||||
if (compressed_bytes == NULL)
|
||||
return CHDERR_READ_ERROR;
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
@ -1600,7 +1635,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
err = CHDERR_NONE;
|
||||
codec = &chd->zlib_codec_data;
|
||||
if (chd->codecintf[0]->decompress != NULL)
|
||||
err = (*chd->codecintf[0]->decompress)(codec, chd->compressed, entry->length, dest, chd->header.hunkbytes);
|
||||
err = (*chd->codecintf[0]->decompress)(codec, compressed_bytes, entry->length, dest, chd->header.hunkbytes);
|
||||
if (err != CHDERR_NONE)
|
||||
return err;
|
||||
#endif
|
||||
@ -1608,21 +1643,21 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
break;
|
||||
|
||||
/* uncompressed data */
|
||||
case MAP_ENTRY_TYPE_UNCOMPRESSED:
|
||||
err = read_uncompressed(chd, entry->offset, chd->header.hunkbytes, dest);
|
||||
case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
|
||||
err = hunk_read_uncompressed(chd, entry->offset, chd->header.hunkbytes, dest);
|
||||
if (err != CHDERR_NONE)
|
||||
return err;
|
||||
break;
|
||||
|
||||
/* mini-compressed data */
|
||||
case MAP_ENTRY_TYPE_MINI:
|
||||
case V34_MAP_ENTRY_TYPE_MINI:
|
||||
put_bigendian_uint64(&dest[0], entry->offset);
|
||||
for (bytes = 8; bytes < chd->header.hunkbytes; bytes++)
|
||||
dest[bytes] = dest[bytes - 8];
|
||||
break;
|
||||
|
||||
/* self-referenced data */
|
||||
case MAP_ENTRY_TYPE_SELF_HUNK:
|
||||
case V34_MAP_ENTRY_TYPE_SELF_HUNK:
|
||||
#ifdef NEED_CACHE_HUNK
|
||||
if (chd->cachehunk == entry->offset && dest == chd->cache)
|
||||
break;
|
||||
@ -1630,7 +1665,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
return hunk_read_into_memory(chd, (UINT32)entry->offset, dest);
|
||||
|
||||
/* parent-referenced data */
|
||||
case MAP_ENTRY_TYPE_PARENT_HUNK:
|
||||
case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
|
||||
err = hunk_read_into_memory(chd->parent, (UINT32)entry->offset, dest);
|
||||
if (err != CHDERR_NONE)
|
||||
return err;
|
||||
@ -1648,7 +1683,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
uint16_t blockcrc;
|
||||
#endif
|
||||
uint8_t *rawmap = &chd->header.rawmap[chd->header.mapentrybytes * hunknum];
|
||||
UINT8 *bytes;
|
||||
UINT8 *compressed_bytes;
|
||||
|
||||
#if 0
|
||||
/* uncompressed case - TODO */
|
||||
@ -1679,9 +1714,11 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
case COMPRESSION_TYPE_1:
|
||||
case COMPRESSION_TYPE_2:
|
||||
case COMPRESSION_TYPE_3:
|
||||
bytes = read_compressed(chd, blockoffs, blocklen);
|
||||
if (bytes == NULL)
|
||||
compressed_bytes = hunk_read_compressed(chd, blockoffs, blocklen);
|
||||
if (compressed_bytes == NULL)
|
||||
return CHDERR_READ_ERROR;
|
||||
if (!chd->codecintf[rawmap[0]])
|
||||
return CHDERR_UNSUPPORTED_FORMAT;
|
||||
switch (chd->codecintf[rawmap[0]]->compression)
|
||||
{
|
||||
case CHD_CODEC_CD_LZMA:
|
||||
@ -1690,6 +1727,12 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
#endif
|
||||
break;
|
||||
|
||||
case CHD_CODEC_ZLIB:
|
||||
#ifdef HAVE_ZLIB
|
||||
codec = &chd->zlib_codec_data;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case CHD_CODEC_CD_ZLIB:
|
||||
#ifdef HAVE_ZLIB
|
||||
codec = &chd->cdzl_codec_data;
|
||||
@ -1704,7 +1747,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
}
|
||||
if (codec==NULL)
|
||||
return CHDERR_CODEC_ERROR;
|
||||
err = (*chd->codecintf[rawmap[0]]->decompress)(codec, chd->compressed, blocklen, dest, chd->header.hunkbytes);
|
||||
err = (*chd->codecintf[rawmap[0]]->decompress)(codec, compressed_bytes, blocklen, dest, chd->header.hunkbytes);
|
||||
if (err != CHDERR_NONE)
|
||||
return err;
|
||||
#ifdef VERIFY_BLOCK_CRC
|
||||
@ -1714,7 +1757,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
return CHDERR_NONE;
|
||||
|
||||
case COMPRESSION_NONE:
|
||||
err = read_uncompressed(chd, blockoffs, blocklen, dest);
|
||||
err = hunk_read_uncompressed(chd, blockoffs, blocklen, dest);
|
||||
if (err != CHDERR_NONE)
|
||||
return err;
|
||||
#ifdef VERIFY_BLOCK_CRC
|
||||
@ -1807,8 +1850,8 @@ static chd_error map_read(chd_file *chd)
|
||||
|
||||
/* track the maximum offset */
|
||||
for (j = 0; j < entries; j++)
|
||||
if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == MAP_ENTRY_TYPE_COMPRESSED ||
|
||||
(chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == MAP_ENTRY_TYPE_UNCOMPRESSED)
|
||||
if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_COMPRESSED ||
|
||||
(chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_UNCOMPRESSED)
|
||||
maxoffset = MAX(maxoffset, chd->map[i + j].offset + chd->map[i + j].length);
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,8 @@ enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decod
|
||||
|
||||
/* binary search to achieve the optimum encoding */
|
||||
upperweight = sdatacount * 2;
|
||||
while (1)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* build a tree using the current weight */
|
||||
uint32_t curweight = (upperweight + lowerweight) / 2;
|
||||
@ -548,16 +549,13 @@ void huffman_build_lookup_table(struct huffman_decoder* decoder)
|
||||
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];
|
||||
int shift = decoder->maxbits - node->numbits;
|
||||
lookup_value *dest = &decoder->lookup[node->bits << shift];
|
||||
lookup_value *destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
|
||||
while (dest <= destend)
|
||||
*dest++ = value;
|
||||
}
|
||||
|
@ -66,11 +66,16 @@
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Huge alignment values for possible SIMD optimization by compiler (NEON, SSE, AVX) */
|
||||
#define LZMA_MIN_ALIGNMENT_BITS 512
|
||||
#define LZMA_MIN_ALIGNMENT_BYTES (LZMA_MIN_ALIGNMENT_BITS / 8)
|
||||
|
||||
static void *lzma_fast_alloc(void *p, size_t size)
|
||||
{
|
||||
int scan;
|
||||
uint32_t *addr = NULL;
|
||||
uint32_t *addr = NULL;
|
||||
lzma_allocator *codec = (lzma_allocator *)(p);
|
||||
uintptr_t vaddr = 0;
|
||||
|
||||
/* compute the size, rounding to the nearest 1k */
|
||||
size = (size + 0x3ff) & ~0x3ff;
|
||||
@ -83,27 +88,36 @@ static void *lzma_fast_alloc(void *p, size_t size)
|
||||
{
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*ptr |= 1;
|
||||
return ptr + 1;
|
||||
|
||||
/* return aligned address of the block */
|
||||
return codec->allocptr2[scan];
|
||||
}
|
||||
}
|
||||
|
||||
/* alloc a new one and put it into the list */
|
||||
addr = (uint32_t *)malloc(sizeof(uint32_t) * (size + sizeof(uint32_t)));
|
||||
if (!addr)
|
||||
addr = (uint32_t *)malloc(size + sizeof(uint32_t) + LZMA_MIN_ALIGNMENT_BYTES);
|
||||
if (addr==NULL)
|
||||
return NULL;
|
||||
|
||||
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
|
||||
{
|
||||
if (codec->allocptr[scan] == NULL)
|
||||
{
|
||||
/* store block address */
|
||||
codec->allocptr[scan] = addr;
|
||||
|
||||
/* compute aligned address, store it */
|
||||
vaddr = (uintptr_t)addr;
|
||||
vaddr = (vaddr + sizeof(uint32_t) + (LZMA_MIN_ALIGNMENT_BYTES-1)) & (~(LZMA_MIN_ALIGNMENT_BYTES-1));
|
||||
codec->allocptr2[scan] = (uint32_t*)vaddr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*addr = (uint32_t)(size | 1);
|
||||
return addr + 1;
|
||||
*addr = size | 1;
|
||||
|
||||
/* return aligned address */
|
||||
return (void*)vaddr;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
@ -114,21 +128,22 @@ static void *lzma_fast_alloc(void *p, size_t size)
|
||||
static void lzma_fast_free(void *p, void *address)
|
||||
{
|
||||
int scan;
|
||||
uint32_t *ptr;
|
||||
lzma_allocator *codec;
|
||||
uint32_t *ptr = NULL;
|
||||
lzma_allocator *codec = NULL;
|
||||
|
||||
if (address == NULL)
|
||||
return;
|
||||
|
||||
codec = (lzma_allocator *)(p);
|
||||
|
||||
/* find the hunk */
|
||||
ptr = (uint32_t *)(address) - 1;
|
||||
ptr = (uint32_t *)address;
|
||||
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
|
||||
{
|
||||
if (ptr == codec->allocptr[scan])
|
||||
if (ptr == codec->allocptr2[scan])
|
||||
{
|
||||
/* clear the low bit of the size to allow matches */
|
||||
*ptr &= ~1;
|
||||
*codec->allocptr[scan] &= ~1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -238,8 +253,8 @@ void lzma_codec_free(void* codec)
|
||||
lzma_allocator* alloc = &lzma_codec->allocator;
|
||||
|
||||
/* free memory */
|
||||
lzma_allocator_free(alloc);
|
||||
LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator);
|
||||
lzma_allocator_free(alloc);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
|
@ -174,6 +174,10 @@ chd_error zlib_codec_init(void *codec, uint32_t hunkbytes)
|
||||
else
|
||||
err = CHDERR_NONE;
|
||||
|
||||
/* handle an error */
|
||||
if (err != CHDERR_NONE)
|
||||
free(data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -225,7 +229,7 @@ chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
|
||||
/* do it */
|
||||
zerr = inflate(&data->inflater, Z_FINISH);
|
||||
(void)zerr;
|
||||
(void)zerr;
|
||||
if (data->inflater.total_out != destlen)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
|
||||
@ -237,9 +241,14 @@ chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
allocates and frees memory frequently
|
||||
-------------------------------------------------*/
|
||||
|
||||
/* Huge alignment values for possible SIMD optimization by compiler (NEON, SSE, AVX) */
|
||||
#define ZLIB_MIN_ALIGNMENT_BITS 512
|
||||
#define ZLIB_MIN_ALIGNMENT_BYTES (ZLIB_MIN_ALIGNMENT_BITS / 8)
|
||||
|
||||
voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size)
|
||||
{
|
||||
zlib_allocator *alloc = (zlib_allocator *)opaque;
|
||||
uintptr_t paddr = 0;
|
||||
UINT32 *ptr;
|
||||
int i;
|
||||
|
||||
@ -254,12 +263,14 @@ voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size)
|
||||
{
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*ptr |= 1;
|
||||
return ptr + 1;
|
||||
|
||||
/* return aligned block address */
|
||||
return (voidpf)(alloc->allocptr2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* alloc a new one */
|
||||
ptr = (UINT32 *)malloc(size + sizeof(UINT32));
|
||||
ptr = (UINT32 *)malloc(size + sizeof(UINT32) + ZLIB_MIN_ALIGNMENT_BYTES);
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
@ -268,12 +279,16 @@ voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size)
|
||||
if (!alloc->allocptr[i])
|
||||
{
|
||||
alloc->allocptr[i] = ptr;
|
||||
paddr = (((uintptr_t)ptr) + sizeof(UINT32) + (ZLIB_MIN_ALIGNMENT_BYTES-1)) & (~(ZLIB_MIN_ALIGNMENT_BYTES-1));
|
||||
alloc->allocptr2[i] = (uint32_t*)paddr;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*ptr = size | 1;
|
||||
return ptr + 1;
|
||||
|
||||
/* return aligned block address */
|
||||
return (voidpf)paddr;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
@ -284,15 +299,15 @@ voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size)
|
||||
void zlib_fast_free(voidpf opaque, voidpf address)
|
||||
{
|
||||
zlib_allocator *alloc = (zlib_allocator *)opaque;
|
||||
UINT32 *ptr = (UINT32 *)address - 1;
|
||||
UINT32 *ptr = (UINT32 *)address;
|
||||
int i;
|
||||
|
||||
/* find the hunk */
|
||||
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
|
||||
if (ptr == alloc->allocptr[i])
|
||||
if (ptr == alloc->allocptr2[i])
|
||||
{
|
||||
/* clear the low bit of the size to allow matches */
|
||||
*ptr &= ~1;
|
||||
*(alloc->allocptr[i]) &= ~1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
455
externals/libretro-common/formats/logiqx_dat/logiqx_dat.c
vendored
Normal file
455
externals/libretro-common/formats/logiqx_dat/logiqx_dat.c
vendored
Normal file
@ -0,0 +1,455 @@
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (logiqx_dat.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <file/file_path.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <formats/rxml.h>
|
||||
|
||||
#include <formats/logiqx_dat.h>
|
||||
|
||||
/* Holds all internal DAT file data */
|
||||
struct logiqx_dat
|
||||
{
|
||||
rxml_document_t *data;
|
||||
rxml_node_t *current_node;
|
||||
};
|
||||
|
||||
/* List of HTML formatting codes that must
|
||||
* be replaced when parsing XML data */
|
||||
const char *logiqx_dat_html_code_list[][2] = {
|
||||
{"&", "&"},
|
||||
{"'", "'"},
|
||||
{">", ">"},
|
||||
{"<", "<"},
|
||||
{""", "\""}
|
||||
};
|
||||
|
||||
#define LOGIQX_DAT_HTML_CODE_LIST_SIZE 5
|
||||
|
||||
/* Validation */
|
||||
|
||||
/* Performs rudimentary validation of the specified
|
||||
* Logiqx XML DAT file path (not rigorous - just
|
||||
* enough to prevent obvious errors).
|
||||
* Also provides access to file size (DAT files can
|
||||
* be very large, so it is useful to have this information
|
||||
* on hand - i.e. so we can check that the system has
|
||||
* enough free memory to load the file). */
|
||||
bool logiqx_dat_path_is_valid(const char *path, uint64_t *file_size)
|
||||
{
|
||||
const char *file_ext = NULL;
|
||||
int32_t file_size_int;
|
||||
|
||||
if (string_is_empty(path))
|
||||
return false;
|
||||
|
||||
/* Check file extension */
|
||||
file_ext = path_get_extension(path);
|
||||
|
||||
if (string_is_empty(file_ext))
|
||||
return false;
|
||||
|
||||
if (!string_is_equal_noncase(file_ext, "dat") &&
|
||||
!string_is_equal_noncase(file_ext, "xml"))
|
||||
return false;
|
||||
|
||||
/* Ensure file exists */
|
||||
if (!path_is_valid(path))
|
||||
return false;
|
||||
|
||||
/* Get file size */
|
||||
file_size_int = path_get_size(path);
|
||||
|
||||
if (file_size_int <= 0)
|
||||
return false;
|
||||
|
||||
if (file_size)
|
||||
*file_size = (uint64_t)file_size_int;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* File initialisation/de-initialisation */
|
||||
|
||||
/* Loads specified Logiqx XML DAT file from disk.
|
||||
* Returned logiqx_dat_t object must be free'd using
|
||||
* logiqx_dat_free().
|
||||
* Returns NULL if file is invalid or a read error
|
||||
* occurs. */
|
||||
logiqx_dat_t *logiqx_dat_init(const char *path)
|
||||
{
|
||||
logiqx_dat_t *dat_file = NULL;
|
||||
rxml_node_t *root_node = NULL;
|
||||
|
||||
/* Check file path */
|
||||
if (!logiqx_dat_path_is_valid(path, NULL))
|
||||
goto error;
|
||||
|
||||
/* Create logiqx_dat_t object */
|
||||
dat_file = (logiqx_dat_t*)calloc(1, sizeof(*dat_file));
|
||||
|
||||
if (!dat_file)
|
||||
goto error;
|
||||
|
||||
/* Read file from disk */
|
||||
dat_file->data = rxml_load_document(path);
|
||||
|
||||
if (!dat_file->data)
|
||||
goto error;
|
||||
|
||||
/* Ensure root node has the correct name */
|
||||
root_node = rxml_root_node(dat_file->data);
|
||||
|
||||
if (!root_node)
|
||||
goto error;
|
||||
|
||||
if (string_is_empty(root_node->name))
|
||||
goto error;
|
||||
|
||||
/* > Logiqx XML uses: 'datafile'
|
||||
* > MAME List XML uses: 'mame'
|
||||
* > MAME 'Software List' uses: 'softwarelist' */
|
||||
if (!string_is_equal(root_node->name, "datafile") &&
|
||||
!string_is_equal(root_node->name, "mame") &&
|
||||
!string_is_equal(root_node->name, "softwarelist"))
|
||||
goto error;
|
||||
|
||||
/* Get pointer to initial child node */
|
||||
dat_file->current_node = root_node->children;
|
||||
|
||||
if (!dat_file->current_node)
|
||||
goto error;
|
||||
|
||||
/* All is well - return logiqx_dat_t object */
|
||||
return dat_file;
|
||||
|
||||
error:
|
||||
logiqx_dat_free(dat_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Frees specified DAT file */
|
||||
void logiqx_dat_free(logiqx_dat_t *dat_file)
|
||||
{
|
||||
if (!dat_file)
|
||||
return;
|
||||
|
||||
dat_file->current_node = NULL;
|
||||
|
||||
if (dat_file->data)
|
||||
{
|
||||
rxml_free_document(dat_file->data);
|
||||
dat_file->data = NULL;
|
||||
}
|
||||
|
||||
free(dat_file);
|
||||
dat_file = NULL;
|
||||
}
|
||||
|
||||
/* Game information access */
|
||||
|
||||
/* Returns true if specified node is a 'game' entry */
|
||||
static bool logiqx_dat_is_game_node(rxml_node_t *node)
|
||||
{
|
||||
const char *node_name = NULL;
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
/* Check node name */
|
||||
node_name = node->name;
|
||||
|
||||
if (string_is_empty(node_name))
|
||||
return false;
|
||||
|
||||
/* > Logiqx XML uses: 'game'
|
||||
* > MAME List XML uses: 'machine'
|
||||
* > MAME 'Software List' uses: 'software' */
|
||||
return string_is_equal(node_name, "game") ||
|
||||
string_is_equal(node_name, "machine") ||
|
||||
string_is_equal(node_name, "software");
|
||||
}
|
||||
|
||||
/* Returns true if specified node is a game
|
||||
* node containing information for a game with
|
||||
* the specified name */
|
||||
static bool logiqx_dat_game_node_matches_name(
|
||||
rxml_node_t *node, const char *game_name)
|
||||
{
|
||||
const char *node_game_name = NULL;
|
||||
|
||||
if (!logiqx_dat_is_game_node(node) ||
|
||||
string_is_empty(game_name))
|
||||
return false;
|
||||
|
||||
/* Get 'name' attribute of XML node */
|
||||
node_game_name = rxml_node_attrib(node, "name");
|
||||
|
||||
if (string_is_empty(node_game_name))
|
||||
return false;
|
||||
|
||||
return string_is_equal(node_game_name, game_name);
|
||||
}
|
||||
|
||||
/* The XML element data strings returned from
|
||||
* DAT files are very 'messy'. This function
|
||||
* removes all cruft, replaces formatting strings
|
||||
* and copies the result (if valid) to 'str' */
|
||||
static void logiqx_dat_sanitise_element_data(
|
||||
const char *data, char *str, size_t len)
|
||||
{
|
||||
char sanitised_data[PATH_MAX_LENGTH];
|
||||
size_t i;
|
||||
|
||||
sanitised_data[0] = '\0';
|
||||
|
||||
if (string_is_empty(data))
|
||||
return;
|
||||
|
||||
strlcpy(sanitised_data, data, sizeof(sanitised_data));
|
||||
|
||||
/* Element data includes leading/trailing
|
||||
* newline characters - trim them away */
|
||||
string_trim_whitespace(sanitised_data);
|
||||
|
||||
if (string_is_empty(sanitised_data))
|
||||
return;
|
||||
|
||||
/* XML has a number of special characters that
|
||||
* are handled using a HTML formatting codes.
|
||||
* All of these have to be replaced...
|
||||
* & -> &
|
||||
* ' -> '
|
||||
* > -> >
|
||||
* < -> <
|
||||
* " -> "
|
||||
*/
|
||||
for (i = 0; i < LOGIQX_DAT_HTML_CODE_LIST_SIZE; i++)
|
||||
{
|
||||
const char *find_string = logiqx_dat_html_code_list[i][0];
|
||||
const char *replace_string = logiqx_dat_html_code_list[i][1];
|
||||
|
||||
/* string_replace_substring() is expensive
|
||||
* > only invoke if element string contains
|
||||
* HTML code */
|
||||
if (strstr(sanitised_data, find_string))
|
||||
{
|
||||
char *tmp = string_replace_substring(
|
||||
sanitised_data, find_string, replace_string);
|
||||
|
||||
if (!string_is_empty(tmp))
|
||||
strlcpy(sanitised_data, tmp, sizeof(sanitised_data));
|
||||
|
||||
if (tmp)
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (string_is_empty(sanitised_data))
|
||||
return;
|
||||
|
||||
/* All is well - can copy result */
|
||||
strlcpy(str, sanitised_data, len);
|
||||
}
|
||||
|
||||
/* Extracts game information from specified node.
|
||||
* Returns false if node is invalid */
|
||||
static bool logiqx_dat_parse_game_node(
|
||||
rxml_node_t *node, logiqx_dat_game_info_t *game_info)
|
||||
{
|
||||
const char *game_name = NULL;
|
||||
const char *is_bios = NULL;
|
||||
const char *is_runnable = NULL;
|
||||
rxml_node_t *info_node = NULL;
|
||||
bool description_found = false;
|
||||
bool year_found = false;
|
||||
bool manufacturer_found = false;
|
||||
|
||||
if (!logiqx_dat_is_game_node(node))
|
||||
return false;
|
||||
|
||||
if (!game_info)
|
||||
return false;
|
||||
|
||||
/* Initialise logiqx_dat_game_info_t object */
|
||||
game_info->name[0] = '\0';
|
||||
game_info->description[0] = '\0';
|
||||
game_info->year[0] = '\0';
|
||||
game_info->manufacturer[0] = '\0';
|
||||
game_info->is_bios = false;
|
||||
game_info->is_runnable = true;
|
||||
|
||||
/* Get game name */
|
||||
game_name = rxml_node_attrib(node, "name");
|
||||
|
||||
if (!string_is_empty(game_name))
|
||||
strlcpy(game_info->name, game_name, sizeof(game_info->name));
|
||||
|
||||
/* Get 'is bios' status */
|
||||
is_bios = rxml_node_attrib(node, "isbios");
|
||||
|
||||
if (!string_is_empty(is_bios))
|
||||
game_info->is_bios = string_is_equal(is_bios, "yes");
|
||||
|
||||
/* Get 'is runnable' status
|
||||
* > Note: This attribute only exists in MAME List
|
||||
* XML files, but there is no harm in checking for
|
||||
* it generally. For normal Logiqx XML files,
|
||||
* 'is runnable' is just the inverse of 'is bios' */
|
||||
is_runnable = rxml_node_attrib(node, "runnable");
|
||||
|
||||
if (!string_is_empty(is_runnable))
|
||||
game_info->is_runnable = string_is_equal(is_runnable, "yes");
|
||||
else
|
||||
game_info->is_runnable = !game_info->is_bios;
|
||||
|
||||
/* Loop over all game info nodes */
|
||||
for (info_node = node->children; info_node; info_node = info_node->next)
|
||||
{
|
||||
const char *info_node_name = info_node->name;
|
||||
const char *info_node_data = info_node->data;
|
||||
|
||||
if (string_is_empty(info_node_name))
|
||||
continue;
|
||||
|
||||
/* Check description */
|
||||
if (string_is_equal(info_node_name, "description"))
|
||||
{
|
||||
logiqx_dat_sanitise_element_data(
|
||||
info_node_data, game_info->description,
|
||||
sizeof(game_info->description));
|
||||
description_found = true;
|
||||
}
|
||||
/* Check year */
|
||||
else if (string_is_equal(info_node_name, "year"))
|
||||
{
|
||||
logiqx_dat_sanitise_element_data(
|
||||
info_node_data, game_info->year,
|
||||
sizeof(game_info->year));
|
||||
year_found = true;
|
||||
}
|
||||
/* Check manufacturer */
|
||||
else if (string_is_equal(info_node_name, "manufacturer"))
|
||||
{
|
||||
logiqx_dat_sanitise_element_data(
|
||||
info_node_data, game_info->manufacturer,
|
||||
sizeof(game_info->manufacturer));
|
||||
manufacturer_found = true;
|
||||
}
|
||||
|
||||
/* If all required entries have been found,
|
||||
* can end loop */
|
||||
if (description_found && year_found && manufacturer_found)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Sets/resets internal node pointer to the first
|
||||
* entry in the DAT file */
|
||||
void logiqx_dat_set_first(logiqx_dat_t *dat_file)
|
||||
{
|
||||
rxml_node_t *root_node = NULL;
|
||||
|
||||
if (!dat_file)
|
||||
return;
|
||||
|
||||
if (!dat_file->data)
|
||||
return;
|
||||
|
||||
/* Get root node */
|
||||
root_node = rxml_root_node(dat_file->data);
|
||||
|
||||
if (!root_node)
|
||||
{
|
||||
dat_file->current_node = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get pointer to initial child node */
|
||||
dat_file->current_node = root_node->children;
|
||||
}
|
||||
|
||||
/* Fetches game information for the current entry
|
||||
* in the DAT file and increments the internal node
|
||||
* pointer.
|
||||
* Returns false if the end of the DAT file has been
|
||||
* reached (in which case 'game_info' will be invalid) */
|
||||
bool logiqx_dat_get_next(
|
||||
logiqx_dat_t *dat_file, logiqx_dat_game_info_t *game_info)
|
||||
{
|
||||
if (!dat_file || !game_info)
|
||||
return false;
|
||||
|
||||
if (!dat_file->data)
|
||||
return false;
|
||||
|
||||
while (dat_file->current_node)
|
||||
{
|
||||
rxml_node_t *current_node = dat_file->current_node;
|
||||
|
||||
/* Whatever happens, internal node pointer must
|
||||
* be 'incremented' */
|
||||
dat_file->current_node = dat_file->current_node->next;
|
||||
|
||||
/* If this is a game node, extract info
|
||||
* and return */
|
||||
if (logiqx_dat_is_game_node(current_node))
|
||||
return logiqx_dat_parse_game_node(current_node, game_info);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fetches information for the specified game.
|
||||
* Returns false if game does not exist, or arguments
|
||||
* are invalid. */
|
||||
bool logiqx_dat_search(
|
||||
logiqx_dat_t *dat_file, const char *game_name,
|
||||
logiqx_dat_game_info_t *game_info)
|
||||
{
|
||||
rxml_node_t *root_node = NULL;
|
||||
rxml_node_t *game_node = NULL;
|
||||
|
||||
if (!dat_file || !game_info || string_is_empty(game_name))
|
||||
return false;
|
||||
|
||||
if (!dat_file->data)
|
||||
return false;
|
||||
|
||||
/* Get root node */
|
||||
root_node = rxml_root_node(dat_file->data);
|
||||
|
||||
if (!root_node)
|
||||
return false;
|
||||
|
||||
/* Loop over all child nodes of the DAT file */
|
||||
for (game_node = root_node->children; game_node; game_node = game_node->next)
|
||||
{
|
||||
/* If this is the requested game, fetch info and return */
|
||||
if (logiqx_dat_game_node_matches_name(game_node, game_name))
|
||||
return logiqx_dat_parse_game_node(game_node, game_info);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
633
externals/libretro-common/formats/m3u/m3u_file.c
vendored
Normal file
633
externals/libretro-common/formats/m3u/m3u_file.c
vendored
Normal file
@ -0,0 +1,633 @@
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (m3u_file.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include <string/stdstring.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <file/file_path.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <array/rbuf.h>
|
||||
|
||||
#include <formats/m3u_file.h>
|
||||
|
||||
/* We parse the following types of entry label:
|
||||
* - '#LABEL:<label>' non-standard, but used by
|
||||
* some cores
|
||||
* - '#EXTINF:<runtime>,<label>' standard extended
|
||||
* M3U directive
|
||||
* - '<content path>|<label>' non-standard, but
|
||||
* used by some cores
|
||||
* All other comments/directives are ignored */
|
||||
#define M3U_FILE_COMMENT '#'
|
||||
#define M3U_FILE_NONSTD_LABEL "#LABEL:"
|
||||
#define M3U_FILE_EXTSTD_LABEL "#EXTINF:"
|
||||
#define M3U_FILE_EXTSTD_LABEL_TOKEN ','
|
||||
#define M3U_FILE_RETRO_LABEL_TOKEN '|'
|
||||
|
||||
/* Holds all internal M3U file data
|
||||
* > Note the awkward name: 'content_m3u_file'
|
||||
* If we used just 'm3u_file' here, it would
|
||||
* lead to conflicts elsewhere... */
|
||||
struct content_m3u_file
|
||||
{
|
||||
char *path;
|
||||
m3u_file_entry_t *entries;
|
||||
};
|
||||
|
||||
/* File Initialisation / De-Initialisation */
|
||||
|
||||
/* Reads M3U file contents from disk
|
||||
* - Does nothing if file does not exist
|
||||
* - Returns false in the event of an error */
|
||||
static bool m3u_file_load(m3u_file_t *m3u_file)
|
||||
{
|
||||
const char *file_ext = NULL;
|
||||
int64_t file_len = 0;
|
||||
uint8_t *file_buf = NULL;
|
||||
struct string_list *lines = NULL;
|
||||
bool success = false;
|
||||
size_t i;
|
||||
char entry_path[PATH_MAX_LENGTH];
|
||||
char entry_label[PATH_MAX_LENGTH];
|
||||
|
||||
entry_path[0] = '\0';
|
||||
entry_label[0] = '\0';
|
||||
|
||||
if (!m3u_file)
|
||||
goto end;
|
||||
|
||||
/* Check whether file exists
|
||||
* > If path is empty, then an error
|
||||
* has occurred... */
|
||||
if (string_is_empty(m3u_file->path))
|
||||
goto end;
|
||||
|
||||
/* > File must have the correct extension */
|
||||
file_ext = path_get_extension(m3u_file->path);
|
||||
|
||||
if (string_is_empty(file_ext) ||
|
||||
!string_is_equal_noncase(file_ext, M3U_FILE_EXT))
|
||||
goto end;
|
||||
|
||||
/* > If file does not exist, no action
|
||||
* is required */
|
||||
if (!path_is_valid(m3u_file->path))
|
||||
{
|
||||
success = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Read file from disk */
|
||||
if (filestream_read_file(m3u_file->path, (void**)&file_buf, &file_len) >= 0)
|
||||
{
|
||||
/* Split file into lines */
|
||||
if (file_len > 0)
|
||||
lines = string_split((const char*)file_buf, "\n");
|
||||
|
||||
/* File buffer no longer required */
|
||||
if (file_buf)
|
||||
{
|
||||
free(file_buf);
|
||||
file_buf = NULL;
|
||||
}
|
||||
}
|
||||
/* File IO error... */
|
||||
else
|
||||
goto end;
|
||||
|
||||
/* If file was empty, no action is required */
|
||||
if (!lines)
|
||||
{
|
||||
success = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Parse lines of file */
|
||||
for (i = 0; i < lines->size; i++)
|
||||
{
|
||||
const char *line = lines->elems[i].data;
|
||||
|
||||
if (string_is_empty(line))
|
||||
continue;
|
||||
|
||||
/* Determine line 'type' */
|
||||
|
||||
/* > '#LABEL:' */
|
||||
if (string_starts_with_size(line, M3U_FILE_NONSTD_LABEL,
|
||||
STRLEN_CONST(M3U_FILE_NONSTD_LABEL)))
|
||||
{
|
||||
/* Label is the string to the right
|
||||
* of '#LABEL:' */
|
||||
const char *label = line + STRLEN_CONST(M3U_FILE_NONSTD_LABEL);
|
||||
|
||||
if (!string_is_empty(label))
|
||||
{
|
||||
strlcpy(
|
||||
entry_label, line + STRLEN_CONST(M3U_FILE_NONSTD_LABEL),
|
||||
sizeof(entry_label));
|
||||
string_trim_whitespace(entry_label);
|
||||
}
|
||||
}
|
||||
/* > '#EXTINF:' */
|
||||
else if (string_starts_with_size(line, M3U_FILE_EXTSTD_LABEL,
|
||||
STRLEN_CONST(M3U_FILE_EXTSTD_LABEL)))
|
||||
{
|
||||
/* Label is the string to the right
|
||||
* of the first comma */
|
||||
const char* label_ptr = strchr(
|
||||
line + STRLEN_CONST(M3U_FILE_EXTSTD_LABEL),
|
||||
M3U_FILE_EXTSTD_LABEL_TOKEN);
|
||||
|
||||
if (!string_is_empty(label_ptr))
|
||||
{
|
||||
label_ptr++;
|
||||
if (!string_is_empty(label_ptr))
|
||||
{
|
||||
strlcpy(entry_label, label_ptr, sizeof(entry_label));
|
||||
string_trim_whitespace(entry_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* > Ignore other comments/directives */
|
||||
else if (line[0] == M3U_FILE_COMMENT)
|
||||
continue;
|
||||
/* > An actual 'content' line */
|
||||
else
|
||||
{
|
||||
/* This is normally a file name/path, but may
|
||||
* have the format <content path>|<label> */
|
||||
const char *token_ptr = strchr(line, M3U_FILE_RETRO_LABEL_TOKEN);
|
||||
|
||||
if (token_ptr)
|
||||
{
|
||||
size_t len = (size_t)(1 + token_ptr - line);
|
||||
|
||||
/* Get entry_path segment */
|
||||
if (len > 0)
|
||||
{
|
||||
memset(entry_path, 0, sizeof(entry_path));
|
||||
strlcpy(
|
||||
entry_path, line,
|
||||
((len < PATH_MAX_LENGTH ?
|
||||
len : PATH_MAX_LENGTH) * sizeof(char)));
|
||||
string_trim_whitespace(entry_path);
|
||||
}
|
||||
|
||||
/* Get entry_label segment */
|
||||
token_ptr++;
|
||||
if (*token_ptr != '\0')
|
||||
{
|
||||
strlcpy(entry_label, token_ptr, sizeof(entry_label));
|
||||
string_trim_whitespace(entry_label);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just a normal file name/path */
|
||||
strlcpy(entry_path, line, sizeof(entry_path));
|
||||
string_trim_whitespace(entry_path);
|
||||
}
|
||||
|
||||
/* Add entry to file
|
||||
* > Note: The only way that m3u_file_add_entry()
|
||||
* can fail here is if we run out of memory.
|
||||
* This is a critical error, and m3u_file must
|
||||
* be considered invalid in this case */
|
||||
if (!string_is_empty(entry_path) &&
|
||||
!m3u_file_add_entry(m3u_file, entry_path, entry_label))
|
||||
goto end;
|
||||
|
||||
/* Reset entry_path/entry_label */
|
||||
entry_path[0] = '\0';
|
||||
entry_label[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
end:
|
||||
/* Clean up */
|
||||
if (lines)
|
||||
{
|
||||
string_list_free(lines);
|
||||
lines = NULL;
|
||||
}
|
||||
|
||||
if (file_buf)
|
||||
{
|
||||
free(file_buf);
|
||||
file_buf = NULL;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Creates and initialises an M3U file
|
||||
* - If 'path' refers to an existing file,
|
||||
* contents is parsed
|
||||
* - If path does not exist, an empty M3U file
|
||||
* is created
|
||||
* - Returned m3u_file_t object must be free'd using
|
||||
* m3u_file_free()
|
||||
* - Returns NULL in the event of an error */
|
||||
m3u_file_t *m3u_file_init(const char *path)
|
||||
{
|
||||
m3u_file_t *m3u_file = NULL;
|
||||
char m3u_path[PATH_MAX_LENGTH];
|
||||
|
||||
m3u_path[0] = '\0';
|
||||
|
||||
/* Sanity check */
|
||||
if (string_is_empty(path))
|
||||
return NULL;
|
||||
|
||||
/* Get 'real' file path */
|
||||
strlcpy(m3u_path, path, sizeof(m3u_path));
|
||||
path_resolve_realpath(m3u_path, sizeof(m3u_path), false);
|
||||
|
||||
if (string_is_empty(m3u_path))
|
||||
return NULL;
|
||||
|
||||
/* Create m3u_file_t object */
|
||||
m3u_file = (m3u_file_t*)malloc(sizeof(*m3u_file));
|
||||
|
||||
if (!m3u_file)
|
||||
return NULL;
|
||||
|
||||
/* Initialise members */
|
||||
m3u_file->path = NULL;
|
||||
m3u_file->entries = NULL;
|
||||
|
||||
/* Copy file path */
|
||||
m3u_file->path = strdup(m3u_path);
|
||||
|
||||
/* Read existing file contents from
|
||||
* disk, if required */
|
||||
if (!m3u_file_load(m3u_file))
|
||||
{
|
||||
m3u_file_free(m3u_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m3u_file;
|
||||
}
|
||||
|
||||
/* Frees specified M3U file entry */
|
||||
static void m3u_file_free_entry(m3u_file_entry_t *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
if (entry->path)
|
||||
free(entry->path);
|
||||
|
||||
if (entry->full_path)
|
||||
free(entry->full_path);
|
||||
|
||||
if (entry->label)
|
||||
free(entry->label);
|
||||
|
||||
entry->path = NULL;
|
||||
entry->full_path = NULL;
|
||||
entry->label = NULL;
|
||||
}
|
||||
|
||||
/* Frees specified M3U file */
|
||||
void m3u_file_free(m3u_file_t *m3u_file)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!m3u_file)
|
||||
return;
|
||||
|
||||
if (m3u_file->path)
|
||||
free(m3u_file->path);
|
||||
|
||||
m3u_file->path = NULL;
|
||||
|
||||
/* Free entries */
|
||||
if (m3u_file->entries)
|
||||
{
|
||||
for (i = 0; i < RBUF_LEN(m3u_file->entries); i++)
|
||||
{
|
||||
m3u_file_entry_t *entry = &m3u_file->entries[i];
|
||||
m3u_file_free_entry(entry);
|
||||
}
|
||||
|
||||
RBUF_FREE(m3u_file->entries);
|
||||
}
|
||||
|
||||
free(m3u_file);
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
|
||||
/* Returns M3U file path */
|
||||
char *m3u_file_get_path(m3u_file_t *m3u_file)
|
||||
{
|
||||
if (!m3u_file)
|
||||
return NULL;
|
||||
|
||||
return m3u_file->path;
|
||||
}
|
||||
|
||||
/* Returns number of entries in M3U file */
|
||||
size_t m3u_file_get_size(m3u_file_t *m3u_file)
|
||||
{
|
||||
if (!m3u_file)
|
||||
return 0;
|
||||
|
||||
return RBUF_LEN(m3u_file->entries);
|
||||
}
|
||||
|
||||
/* Fetches specified M3U file entry
|
||||
* - Returns false if 'idx' is invalid, or internal
|
||||
* entry is NULL */
|
||||
bool m3u_file_get_entry(
|
||||
m3u_file_t *m3u_file, size_t idx, m3u_file_entry_t **entry)
|
||||
{
|
||||
if (!m3u_file ||
|
||||
!entry ||
|
||||
(idx >= RBUF_LEN(m3u_file->entries)))
|
||||
return false;
|
||||
|
||||
*entry = &m3u_file->entries[idx];
|
||||
|
||||
if (!*entry)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Setters */
|
||||
|
||||
/* Adds specified entry to the M3U file
|
||||
* - Returns false if path is invalid, or
|
||||
* memory could not be allocated for the
|
||||
* entry */
|
||||
bool m3u_file_add_entry(
|
||||
m3u_file_t *m3u_file, const char *path, const char *label)
|
||||
{
|
||||
m3u_file_entry_t *entry = NULL;
|
||||
size_t num_entries;
|
||||
char full_path[PATH_MAX_LENGTH];
|
||||
|
||||
full_path[0] = '\0';
|
||||
|
||||
if (!m3u_file || string_is_empty(path))
|
||||
return false;
|
||||
|
||||
/* Get current number of file entries */
|
||||
num_entries = RBUF_LEN(m3u_file->entries);
|
||||
|
||||
/* Attempt to allocate memory for new entry */
|
||||
if (!RBUF_TRYFIT(m3u_file->entries, num_entries + 1))
|
||||
return false;
|
||||
|
||||
/* Allocation successful - increment array size */
|
||||
RBUF_RESIZE(m3u_file->entries, num_entries + 1);
|
||||
|
||||
/* Fetch entry at end of list, and zero-initialise
|
||||
* members */
|
||||
entry = &m3u_file->entries[num_entries];
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
/* Copy path and label */
|
||||
entry->path = strdup(path);
|
||||
|
||||
if (!string_is_empty(label))
|
||||
entry->label = strdup(label);
|
||||
|
||||
/* Populate 'full_path' field */
|
||||
if (path_is_absolute(path))
|
||||
{
|
||||
strlcpy(full_path, path, sizeof(full_path));
|
||||
path_resolve_realpath(full_path, sizeof(full_path), false);
|
||||
}
|
||||
else
|
||||
fill_pathname_resolve_relative(
|
||||
full_path, m3u_file->path, path,
|
||||
sizeof(full_path));
|
||||
|
||||
/* Handle unforeseen errors... */
|
||||
if (string_is_empty(full_path))
|
||||
{
|
||||
m3u_file_free_entry(entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
entry->full_path = strdup(full_path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Removes all entries in M3U file */
|
||||
void m3u_file_clear(m3u_file_t *m3u_file)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!m3u_file)
|
||||
return;
|
||||
|
||||
if (m3u_file->entries)
|
||||
{
|
||||
for (i = 0; i < RBUF_LEN(m3u_file->entries); i++)
|
||||
{
|
||||
m3u_file_entry_t *entry = &m3u_file->entries[i];
|
||||
m3u_file_free_entry(entry);
|
||||
}
|
||||
|
||||
RBUF_FREE(m3u_file->entries);
|
||||
}
|
||||
}
|
||||
|
||||
/* Saving */
|
||||
|
||||
/* Saves M3U file to disk
|
||||
* - Setting 'label_type' to M3U_FILE_LABEL_NONE
|
||||
* just outputs entry paths - this the most
|
||||
* common format supported by most cores
|
||||
* - Returns false in the event of an error */
|
||||
bool m3u_file_save(
|
||||
m3u_file_t *m3u_file, enum m3u_file_label_type label_type)
|
||||
{
|
||||
RFILE *file = NULL;
|
||||
size_t i;
|
||||
char base_dir[PATH_MAX_LENGTH];
|
||||
|
||||
base_dir[0] = '\0';
|
||||
|
||||
if (!m3u_file || !m3u_file->entries)
|
||||
return false;
|
||||
|
||||
/* This should never happen */
|
||||
if (string_is_empty(m3u_file->path))
|
||||
return false;
|
||||
|
||||
/* Get M3U file base directory */
|
||||
if (find_last_slash(m3u_file->path))
|
||||
{
|
||||
strlcpy(base_dir, m3u_file->path, sizeof(base_dir));
|
||||
path_basedir(base_dir);
|
||||
}
|
||||
|
||||
/* Open file for writing */
|
||||
file = filestream_open(
|
||||
m3u_file->path,
|
||||
RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
/* Loop over entries */
|
||||
for (i = 0; i < RBUF_LEN(m3u_file->entries); i++)
|
||||
{
|
||||
m3u_file_entry_t *entry = &m3u_file->entries[i];
|
||||
char entry_path[PATH_MAX_LENGTH];
|
||||
|
||||
entry_path[0] = '\0';
|
||||
|
||||
if (!entry || string_is_empty(entry->full_path))
|
||||
continue;
|
||||
|
||||
/* When writing M3U files, entry paths are
|
||||
* always relative */
|
||||
if (string_is_empty(base_dir))
|
||||
strlcpy(
|
||||
entry_path, entry->full_path,
|
||||
sizeof(entry_path));
|
||||
else
|
||||
path_relative_to(
|
||||
entry_path, entry->full_path, base_dir,
|
||||
sizeof(entry_path));
|
||||
|
||||
if (string_is_empty(entry_path))
|
||||
continue;
|
||||
|
||||
/* Check if we need to write a label */
|
||||
if (!string_is_empty(entry->label))
|
||||
{
|
||||
switch (label_type)
|
||||
{
|
||||
case M3U_FILE_LABEL_NONSTD:
|
||||
filestream_printf(
|
||||
file, "%s%s\n%s\n",
|
||||
M3U_FILE_NONSTD_LABEL, entry->label,
|
||||
entry_path);
|
||||
break;
|
||||
case M3U_FILE_LABEL_EXTSTD:
|
||||
filestream_printf(
|
||||
file, "%s%c%s\n%s\n",
|
||||
M3U_FILE_EXTSTD_LABEL, M3U_FILE_EXTSTD_LABEL_TOKEN, entry->label,
|
||||
entry_path);
|
||||
break;
|
||||
case M3U_FILE_LABEL_RETRO:
|
||||
filestream_printf(
|
||||
file, "%s%c%s\n",
|
||||
entry_path, M3U_FILE_RETRO_LABEL_TOKEN, entry->label);
|
||||
break;
|
||||
case M3U_FILE_LABEL_NONE:
|
||||
default:
|
||||
filestream_printf(
|
||||
file, "%s\n", entry_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* No label - just write entry path */
|
||||
else
|
||||
filestream_printf(
|
||||
file, "%s\n", entry_path);
|
||||
}
|
||||
|
||||
/* Close file */
|
||||
filestream_close(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
/* Internal qsort function */
|
||||
static int m3u_file_qsort_func(
|
||||
const m3u_file_entry_t *a, const m3u_file_entry_t *b)
|
||||
{
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
|
||||
if (string_is_empty(a->full_path) || string_is_empty(b->full_path))
|
||||
return 0;
|
||||
|
||||
return strcasecmp(a->full_path, b->full_path);
|
||||
}
|
||||
|
||||
/* Sorts M3U file entries in alphabetical order */
|
||||
void m3u_file_qsort(m3u_file_t *m3u_file)
|
||||
{
|
||||
size_t num_entries;
|
||||
|
||||
if (!m3u_file)
|
||||
return;
|
||||
|
||||
num_entries = RBUF_LEN(m3u_file->entries);
|
||||
|
||||
if (num_entries < 2)
|
||||
return;
|
||||
|
||||
qsort(
|
||||
m3u_file->entries, num_entries,
|
||||
sizeof(m3u_file_entry_t),
|
||||
(int (*)(const void *, const void *))m3u_file_qsort_func);
|
||||
}
|
||||
|
||||
/* Returns true if specified path corresponds
|
||||
* to an M3U file (simple convenience function) */
|
||||
bool m3u_file_is_m3u(const char *path)
|
||||
{
|
||||
const char *file_ext = NULL;
|
||||
int32_t file_size;
|
||||
|
||||
if (string_is_empty(path))
|
||||
return false;
|
||||
|
||||
/* Check file extension */
|
||||
file_ext = path_get_extension(path);
|
||||
|
||||
if (string_is_empty(file_ext))
|
||||
return false;
|
||||
|
||||
if (!string_is_equal_noncase(file_ext, M3U_FILE_EXT))
|
||||
return false;
|
||||
|
||||
/* Ensure file exists */
|
||||
if (!path_is_valid(path))
|
||||
return false;
|
||||
|
||||
/* Ensure we have non-zero file size */
|
||||
file_size = path_get_size(path);
|
||||
|
||||
if (file_size <= 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
445
externals/libretro-common/formats/png/rpng.c
vendored
445
externals/libretro-common/formats/png/rpng.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rpng.c).
|
||||
@ -20,7 +20,9 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -80,22 +82,15 @@ struct idat_buffer
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct png_chunk
|
||||
{
|
||||
uint32_t size;
|
||||
char type[4];
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct rpng_process
|
||||
{
|
||||
bool inflate_initialized;
|
||||
bool adam7_pass_initialized;
|
||||
bool pass_initialized;
|
||||
uint32_t *data;
|
||||
uint32_t *palette;
|
||||
void *stream;
|
||||
const struct trans_stream_backend *stream_backend;
|
||||
uint8_t *prev_scanline;
|
||||
uint8_t *decoded_scanline;
|
||||
uint8_t *inflate_buf;
|
||||
struct png_ihdr ihdr;
|
||||
size_t restore_buf_size;
|
||||
size_t adam7_restore_buf_size;
|
||||
size_t data_restore_buf_size;
|
||||
@ -104,31 +99,41 @@ struct rpng_process
|
||||
size_t avail_out;
|
||||
size_t total_out;
|
||||
size_t pass_size;
|
||||
struct png_ihdr ihdr; /* uint32_t alignment */
|
||||
unsigned bpp;
|
||||
unsigned pitch;
|
||||
unsigned h;
|
||||
unsigned pass_width;
|
||||
unsigned pass_height;
|
||||
unsigned pass_pos;
|
||||
uint32_t *data;
|
||||
uint32_t *palette;
|
||||
void *stream;
|
||||
const struct trans_stream_backend *stream_backend;
|
||||
bool inflate_initialized;
|
||||
bool adam7_pass_initialized;
|
||||
bool pass_initialized;
|
||||
};
|
||||
|
||||
struct rpng
|
||||
{
|
||||
struct rpng_process *process;
|
||||
uint8_t *buff_data;
|
||||
uint8_t *buff_end;
|
||||
struct idat_buffer idat_buf; /* ptr alignment */
|
||||
struct png_ihdr ihdr; /* uint32 alignment */
|
||||
uint32_t palette[256];
|
||||
bool has_ihdr;
|
||||
bool has_idat;
|
||||
bool has_iend;
|
||||
bool has_plte;
|
||||
bool has_trns;
|
||||
struct idat_buffer idat_buf;
|
||||
struct png_ihdr ihdr;
|
||||
uint8_t *buff_data;
|
||||
uint8_t *buff_end;
|
||||
uint32_t palette[256];
|
||||
};
|
||||
|
||||
static const struct adam7_pass passes[] = {
|
||||
{ 0, 0, 8, 8 },
|
||||
{ 4, 0, 8, 8 },
|
||||
{ 0, 4, 4, 8 },
|
||||
{ 2, 0, 4, 4 },
|
||||
{ 0, 2, 2, 4 },
|
||||
{ 1, 0, 2, 2 },
|
||||
{ 0, 1, 1, 2 },
|
||||
};
|
||||
|
||||
static INLINE uint32_t dword_be(const uint8_t *buf)
|
||||
@ -136,98 +141,85 @@ static INLINE uint32_t dword_be(const uint8_t *buf)
|
||||
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0);
|
||||
}
|
||||
|
||||
static enum png_chunk_type png_chunk_type(const struct png_chunk *chunk)
|
||||
{
|
||||
unsigned i;
|
||||
struct
|
||||
{
|
||||
const char *id;
|
||||
enum png_chunk_type type;
|
||||
} static const chunk_map[] = {
|
||||
{ "IHDR", PNG_CHUNK_IHDR },
|
||||
{ "IDAT", PNG_CHUNK_IDAT },
|
||||
{ "IEND", PNG_CHUNK_IEND },
|
||||
{ "PLTE", PNG_CHUNK_PLTE },
|
||||
{ "tRNS", PNG_CHUNK_tRNS },
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chunk_map); i++)
|
||||
{
|
||||
if (string_is_equal(chunk->type, chunk_map[i].id))
|
||||
return chunk_map[i].type;
|
||||
}
|
||||
|
||||
return PNG_CHUNK_NOOP;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(RPNG_TEST)
|
||||
static bool png_process_ihdr(struct png_ihdr *ihdr)
|
||||
{
|
||||
unsigned i;
|
||||
bool ret = true;
|
||||
uint8_t ihdr_depth = ihdr->depth;
|
||||
|
||||
switch (ihdr->color_type)
|
||||
{
|
||||
case PNG_IHDR_COLOR_RGB:
|
||||
case PNG_IHDR_COLOR_GRAY_ALPHA:
|
||||
case PNG_IHDR_COLOR_RGBA:
|
||||
if (ihdr->depth != 8 && ihdr->depth != 16)
|
||||
GOTO_END_ERROR();
|
||||
if (ihdr_depth != 8 && ihdr_depth != 16)
|
||||
{
|
||||
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case PNG_IHDR_COLOR_GRAY:
|
||||
/* Valid bitdepths are: 1, 2, 4, 8, 16 */
|
||||
if (ihdr_depth > 16 || (0x977F7FFF << ihdr_depth) & 0x80000000)
|
||||
{
|
||||
static const unsigned valid_bpp[] = { 1, 2, 4, 8, 16 };
|
||||
bool correct_bpp = false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(valid_bpp); i++)
|
||||
{
|
||||
if (valid_bpp[i] == ihdr->depth)
|
||||
{
|
||||
correct_bpp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!correct_bpp)
|
||||
GOTO_END_ERROR();
|
||||
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case PNG_IHDR_COLOR_PLT:
|
||||
/* Valid bitdepths are: 1, 2, 4, 8 */
|
||||
if (ihdr_depth > 8 || (0x977F7FFF << ihdr_depth) & 0x80000000)
|
||||
{
|
||||
static const unsigned valid_bpp[] = { 1, 2, 4, 8 };
|
||||
bool correct_bpp = false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(valid_bpp); i++)
|
||||
{
|
||||
if (valid_bpp[i] == ihdr->depth)
|
||||
{
|
||||
correct_bpp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!correct_bpp)
|
||||
GOTO_END_ERROR();
|
||||
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GOTO_END_ERROR();
|
||||
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef RPNG_TEST
|
||||
fprintf(stderr, "IHDR: (%u x %u), bpc = %u, palette = %s, color = %s, alpha = %s, adam7 = %s.\n",
|
||||
ihdr->width, ihdr->height,
|
||||
ihdr->depth, (ihdr->color_type == PNG_IHDR_COLOR_PLT) ? "yes" : "no",
|
||||
(ihdr->color_type & PNG_IHDR_COLOR_RGB) ? "yes" : "no",
|
||||
(ihdr->color_type & PNG_IHDR_COLOR_GRAY_ALPHA) ? "yes" : "no",
|
||||
ihdr_depth, (ihdr->color_type == PNG_IHDR_COLOR_PLT) ? "yes" : "no",
|
||||
(ihdr->color_type & PNG_IHDR_COLOR_RGB) ? "yes" : "no",
|
||||
(ihdr->color_type & PNG_IHDR_COLOR_GRAY_ALPHA) ? "yes" : "no",
|
||||
ihdr->interlace == 1 ? "yes" : "no");
|
||||
#endif
|
||||
|
||||
if (ihdr->compression != 0)
|
||||
GOTO_END_ERROR();
|
||||
|
||||
end:
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static bool png_process_ihdr(struct png_ihdr *ihdr)
|
||||
{
|
||||
uint8_t ihdr_depth = ihdr->depth;
|
||||
|
||||
switch (ihdr->color_type)
|
||||
{
|
||||
case PNG_IHDR_COLOR_RGB:
|
||||
case PNG_IHDR_COLOR_GRAY_ALPHA:
|
||||
case PNG_IHDR_COLOR_RGBA:
|
||||
if (ihdr_depth != 8 && ihdr_depth != 16)
|
||||
return false;
|
||||
break;
|
||||
case PNG_IHDR_COLOR_GRAY:
|
||||
/* Valid bitdepths are: 1, 2, 4, 8, 16 */
|
||||
if (ihdr_depth > 16 || (0x977F7FFF << ihdr_depth) & 0x80000000)
|
||||
return false;
|
||||
break;
|
||||
case PNG_IHDR_COLOR_PLT:
|
||||
/* Valid bitdepths are: 1, 2, 4, 8 */
|
||||
if (ihdr_depth > 8 || (0x977F7FFF << ihdr_depth) & 0x80000000)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void png_reverse_filter_copy_line_rgb(uint32_t *data,
|
||||
const uint8_t *decoded, unsigned width, unsigned bpp)
|
||||
@ -407,9 +399,7 @@ static void png_reverse_filter_copy_line_plt(uint32_t *data,
|
||||
}
|
||||
|
||||
if (width & 1)
|
||||
{
|
||||
*data = palette[*decoded >> 4];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -418,9 +408,7 @@ static void png_reverse_filter_copy_line_plt(uint32_t *data,
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < width; i++, decoded++, data++)
|
||||
{
|
||||
*data = palette[*decoded];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -430,8 +418,8 @@ static void png_pass_geom(const struct png_ihdr *ihdr,
|
||||
unsigned width, unsigned height,
|
||||
unsigned *bpp_out, unsigned *pitch_out, size_t *pass_size)
|
||||
{
|
||||
unsigned bpp;
|
||||
unsigned pitch;
|
||||
unsigned bpp = 0;
|
||||
unsigned pitch = 0;
|
||||
|
||||
switch (ihdr->color_type)
|
||||
{
|
||||
@ -456,15 +444,13 @@ static void png_pass_geom(const struct png_ihdr *ihdr,
|
||||
pitch = (ihdr->width * ihdr->depth * 4 + 7) / 8;
|
||||
break;
|
||||
default:
|
||||
bpp = 0;
|
||||
pitch = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pass_size)
|
||||
*pass_size = (pitch + 1) * ihdr->height;
|
||||
if (bpp_out)
|
||||
*bpp_out = bpp;
|
||||
*bpp_out = bpp;
|
||||
if (pitch_out)
|
||||
*pitch_out = pitch;
|
||||
}
|
||||
@ -503,16 +489,6 @@ static void png_reverse_filter_deinit(struct rpng_process *pngp)
|
||||
pngp->h = 0;
|
||||
}
|
||||
|
||||
static const struct adam7_pass passes[] = {
|
||||
{ 0, 0, 8, 8 },
|
||||
{ 4, 0, 8, 8 },
|
||||
{ 0, 4, 4, 8 },
|
||||
{ 2, 0, 4, 4 },
|
||||
{ 0, 2, 2, 4 },
|
||||
{ 1, 0, 2, 2 },
|
||||
{ 0, 1, 1, 2 },
|
||||
};
|
||||
|
||||
static int png_reverse_filter_init(const struct png_ihdr *ihdr,
|
||||
struct rpng_process *pngp)
|
||||
{
|
||||
@ -705,10 +681,11 @@ static int png_reverse_filter_adam7_iterate(uint32_t **data_,
|
||||
if (png_reverse_filter_init(&pngp->ihdr, pngp) == -1)
|
||||
return IMAGE_PROCESS_ERROR;
|
||||
|
||||
do{
|
||||
do
|
||||
{
|
||||
ret = png_reverse_filter_regular_iterate(&pngp->data,
|
||||
&pngp->ihdr, pngp);
|
||||
}while(ret == IMAGE_PROCESS_NEXT);
|
||||
} while (ret == IMAGE_PROCESS_NEXT);
|
||||
|
||||
if (ret == IMAGE_PROCESS_ERROR || ret == IMAGE_PROCESS_ERROR_END)
|
||||
return IMAGE_PROCESS_ERROR;
|
||||
@ -856,9 +833,9 @@ static bool png_read_trns(uint8_t *buf, uint32_t *palette, unsigned entries)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool png_realloc_idat(const struct png_chunk *chunk, struct idat_buffer *buf)
|
||||
bool png_realloc_idat(struct idat_buffer *buf, uint32_t chunk_size)
|
||||
{
|
||||
uint8_t *new_buffer = (uint8_t*)realloc(buf->data, buf->size + chunk->size);
|
||||
uint8_t *new_buffer = (uint8_t*)realloc(buf->data, buf->size + chunk_size);
|
||||
|
||||
if (!new_buffer)
|
||||
return false;
|
||||
@ -869,13 +846,45 @@ bool png_realloc_idat(const struct png_chunk *chunk, struct idat_buffer *buf)
|
||||
|
||||
static struct rpng_process *rpng_process_init(rpng_t *rpng)
|
||||
{
|
||||
uint8_t *inflate_buf = NULL;
|
||||
struct rpng_process *process = (struct rpng_process*)calloc(1, sizeof(*process));
|
||||
uint8_t *inflate_buf = NULL;
|
||||
struct rpng_process *process = (struct rpng_process*)malloc(sizeof(*process));
|
||||
|
||||
if (!process)
|
||||
return NULL;
|
||||
|
||||
process->stream_backend = trans_stream_get_zlib_inflate_backend();
|
||||
process->inflate_initialized = false;
|
||||
process->adam7_pass_initialized = false;
|
||||
process->pass_initialized = false;
|
||||
process->prev_scanline = NULL;
|
||||
process->decoded_scanline = NULL;
|
||||
process->inflate_buf = NULL;
|
||||
|
||||
process->ihdr.width = 0;
|
||||
process->ihdr.height = 0;
|
||||
process->ihdr.depth = 0;
|
||||
process->ihdr.color_type = 0;
|
||||
process->ihdr.compression = 0;
|
||||
process->ihdr.filter = 0;
|
||||
process->ihdr.interlace = 0;
|
||||
|
||||
process->restore_buf_size = 0;
|
||||
process->adam7_restore_buf_size = 0;
|
||||
process->data_restore_buf_size = 0;
|
||||
process->inflate_buf_size = 0;
|
||||
process->avail_in = 0;
|
||||
process->avail_out = 0;
|
||||
process->total_out = 0;
|
||||
process->pass_size = 0;
|
||||
process->bpp = 0;
|
||||
process->pitch = 0;
|
||||
process->h = 0;
|
||||
process->pass_width = 0;
|
||||
process->pass_height = 0;
|
||||
process->pass_pos = 0;
|
||||
process->data = 0;
|
||||
process->palette = 0;
|
||||
process->stream = NULL;
|
||||
process->stream_backend = trans_stream_get_zlib_inflate_backend();
|
||||
|
||||
png_pass_geom(&rpng->ihdr, rpng->ihdr.width,
|
||||
rpng->ihdr.height, NULL, NULL, &process->inflate_buf_size);
|
||||
@ -895,9 +904,9 @@ static struct rpng_process *rpng_process_init(rpng_t *rpng)
|
||||
goto error;
|
||||
|
||||
process->inflate_buf = inflate_buf;
|
||||
process->avail_in = rpng->idat_buf.size;
|
||||
process->avail_out = process->inflate_buf_size;
|
||||
process->total_out = 0;
|
||||
process->avail_in = rpng->idat_buf.size;
|
||||
process->avail_out = process->inflate_buf_size;
|
||||
|
||||
process->stream_backend->set_in(
|
||||
process->stream,
|
||||
rpng->idat_buf.data,
|
||||
@ -919,26 +928,11 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool read_chunk_header(uint8_t *buf, uint8_t *buf_end, struct png_chunk *chunk)
|
||||
static enum png_chunk_type read_chunk_header(
|
||||
uint8_t *buf, uint32_t chunk_size)
|
||||
{
|
||||
unsigned i;
|
||||
uint8_t dword[4];
|
||||
|
||||
dword[0] = '\0';
|
||||
|
||||
/* Check whether reading the header will overflow
|
||||
* the data buffer */
|
||||
if (buf_end - buf < 8)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
dword[i] = buf[i];
|
||||
|
||||
chunk->size = dword_be(dword);
|
||||
|
||||
/* Check whether chunk will overflow the data buffer */
|
||||
if (buf + 8 + chunk->size > buf_end)
|
||||
return false;
|
||||
char type[4];
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
@ -947,99 +941,139 @@ static bool read_chunk_header(uint8_t *buf, uint8_t *buf_end, struct png_chunk *
|
||||
/* All four bytes of the chunk type must be
|
||||
* ASCII letters (codes 65-90 and 97-122) */
|
||||
if ((byte < 65) || ((byte > 90) && (byte < 97)) || (byte > 122))
|
||||
return false;
|
||||
|
||||
chunk->type[i] = byte;
|
||||
return PNG_CHUNK_ERROR;
|
||||
type[i] = byte;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
type[0] == 'I'
|
||||
&& type[1] == 'H'
|
||||
&& type[2] == 'D'
|
||||
&& type[3] == 'R'
|
||||
)
|
||||
return PNG_CHUNK_IHDR;
|
||||
else if
|
||||
(
|
||||
type[0] == 'I'
|
||||
&& type[1] == 'D'
|
||||
&& type[2] == 'A'
|
||||
&& type[3] == 'T'
|
||||
)
|
||||
return PNG_CHUNK_IDAT;
|
||||
else if
|
||||
(
|
||||
type[0] == 'I'
|
||||
&& type[1] == 'E'
|
||||
&& type[2] == 'N'
|
||||
&& type[3] == 'D'
|
||||
)
|
||||
return PNG_CHUNK_IEND;
|
||||
else if
|
||||
(
|
||||
type[0] == 'P'
|
||||
&& type[1] == 'L'
|
||||
&& type[2] == 'T'
|
||||
&& type[3] == 'E'
|
||||
)
|
||||
return PNG_CHUNK_PLTE;
|
||||
else if
|
||||
(
|
||||
type[0] == 't'
|
||||
&& type[1] == 'R'
|
||||
&& type[2] == 'N'
|
||||
&& type[3] == 'S'
|
||||
)
|
||||
return PNG_CHUNK_tRNS;
|
||||
|
||||
static bool png_parse_ihdr(uint8_t *buf,
|
||||
struct png_ihdr *ihdr)
|
||||
{
|
||||
buf += 4 + 4;
|
||||
|
||||
ihdr->width = dword_be(buf + 0);
|
||||
ihdr->height = dword_be(buf + 4);
|
||||
ihdr->depth = buf[8];
|
||||
ihdr->color_type = buf[9];
|
||||
ihdr->compression = buf[10];
|
||||
ihdr->filter = buf[11];
|
||||
ihdr->interlace = buf[12];
|
||||
|
||||
if (ihdr->width == 0 || ihdr->height == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return PNG_CHUNK_NOOP;
|
||||
}
|
||||
|
||||
bool rpng_iterate_image(rpng_t *rpng)
|
||||
{
|
||||
unsigned i;
|
||||
struct png_chunk chunk;
|
||||
uint8_t *buf = (uint8_t*)rpng->buff_data;
|
||||
|
||||
chunk.size = 0;
|
||||
chunk.type[0] = 0;
|
||||
chunk.data = NULL;
|
||||
uint8_t *buf = (uint8_t*)rpng->buff_data;
|
||||
uint32_t chunk_size = 0;
|
||||
|
||||
/* Check whether data buffer pointer is valid */
|
||||
if (buf > rpng->buff_end)
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
if (!read_chunk_header(buf, rpng->buff_end, &chunk))
|
||||
goto error;
|
||||
/* Check whether reading the header will overflow
|
||||
* the data buffer */
|
||||
if (rpng->buff_end - buf < 8)
|
||||
return false;
|
||||
|
||||
#if 0
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
fprintf(stderr, "chunktype: %c\n", chunk.type[i]);
|
||||
}
|
||||
#endif
|
||||
chunk_size = dword_be(buf);
|
||||
|
||||
switch (png_chunk_type(&chunk))
|
||||
/* Check whether chunk will overflow the data buffer */
|
||||
if (buf + 8 + chunk_size > rpng->buff_end)
|
||||
return false;
|
||||
|
||||
switch (read_chunk_header(buf, chunk_size))
|
||||
{
|
||||
case PNG_CHUNK_NOOP:
|
||||
default:
|
||||
break;
|
||||
|
||||
case PNG_CHUNK_ERROR:
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
case PNG_CHUNK_IHDR:
|
||||
if (rpng->has_ihdr || rpng->has_idat || rpng->has_iend)
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
if (chunk.size != 13)
|
||||
goto error;
|
||||
if (chunk_size != 13)
|
||||
return false;
|
||||
|
||||
if (!png_parse_ihdr(buf, &rpng->ihdr))
|
||||
goto error;
|
||||
buf += 4 + 4;
|
||||
|
||||
rpng->ihdr.width = dword_be(buf + 0);
|
||||
rpng->ihdr.height = dword_be(buf + 4);
|
||||
rpng->ihdr.depth = buf[8];
|
||||
rpng->ihdr.color_type = buf[9];
|
||||
rpng->ihdr.compression = buf[10];
|
||||
rpng->ihdr.filter = buf[11];
|
||||
rpng->ihdr.interlace = buf[12];
|
||||
|
||||
if ( rpng->ihdr.width == 0
|
||||
|| rpng->ihdr.height == 0)
|
||||
return false;
|
||||
|
||||
if (!png_process_ihdr(&rpng->ihdr))
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
if (rpng->ihdr.compression != 0)
|
||||
{
|
||||
#if defined(DEBUG) || defined(RPNG_TEST)
|
||||
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
rpng->has_ihdr = true;
|
||||
break;
|
||||
|
||||
case PNG_CHUNK_PLTE:
|
||||
{
|
||||
unsigned entries = chunk.size / 3;
|
||||
unsigned entries = chunk_size / 3;
|
||||
|
||||
if (!rpng->has_ihdr || rpng->has_plte || rpng->has_iend || rpng->has_idat || rpng->has_trns)
|
||||
goto error;
|
||||
if ( !rpng->has_ihdr
|
||||
|| rpng->has_plte
|
||||
|| rpng->has_iend
|
||||
|| rpng->has_idat
|
||||
|| rpng->has_trns)
|
||||
return false;
|
||||
|
||||
if (chunk.size % 3)
|
||||
goto error;
|
||||
if (chunk_size % 3)
|
||||
return false;
|
||||
|
||||
if (entries > 256)
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
buf += 8;
|
||||
|
||||
if (!png_read_plte(buf, rpng->palette, entries))
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
rpng->has_plte = true;
|
||||
}
|
||||
@ -1047,18 +1081,18 @@ bool rpng_iterate_image(rpng_t *rpng)
|
||||
|
||||
case PNG_CHUNK_tRNS:
|
||||
if (rpng->has_idat)
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
if (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT)
|
||||
{
|
||||
/* we should compare with the number of palette entries */
|
||||
if (chunk.size > 256)
|
||||
goto error;
|
||||
if (chunk_size > 256)
|
||||
return false;
|
||||
|
||||
buf += 8;
|
||||
|
||||
if (!png_read_trns(buf, rpng->palette, chunk.size))
|
||||
goto error;
|
||||
if (!png_read_trns(buf, rpng->palette, chunk_size))
|
||||
return false;
|
||||
}
|
||||
/* TODO: support colorkey in grayscale and truecolor images */
|
||||
|
||||
@ -1067,39 +1101,35 @@ bool rpng_iterate_image(rpng_t *rpng)
|
||||
|
||||
case PNG_CHUNK_IDAT:
|
||||
if (!(rpng->has_ihdr) || rpng->has_iend || (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT && !(rpng->has_plte)))
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
if (!png_realloc_idat(&chunk, &rpng->idat_buf))
|
||||
goto error;
|
||||
if (!png_realloc_idat(&rpng->idat_buf, chunk_size))
|
||||
return false;
|
||||
|
||||
buf += 8;
|
||||
|
||||
for (i = 0; i < chunk.size; i++)
|
||||
for (i = 0; i < chunk_size; i++)
|
||||
rpng->idat_buf.data[i + rpng->idat_buf.size] = buf[i];
|
||||
|
||||
rpng->idat_buf.size += chunk.size;
|
||||
rpng->idat_buf.size += chunk_size;
|
||||
|
||||
rpng->has_idat = true;
|
||||
break;
|
||||
|
||||
case PNG_CHUNK_IEND:
|
||||
if (!(rpng->has_ihdr) || !(rpng->has_idat))
|
||||
goto error;
|
||||
return false;
|
||||
|
||||
rpng->has_iend = true;
|
||||
goto error;
|
||||
return false;
|
||||
}
|
||||
|
||||
rpng->buff_data += chunk.size + 12;
|
||||
rpng->buff_data += chunk_size + 12;
|
||||
|
||||
/* Check whether data buffer pointer is valid */
|
||||
if (rpng->buff_data > rpng->buff_end)
|
||||
goto error;
|
||||
|
||||
return false;
|
||||
return true;
|
||||
|
||||
error:
|
||||
return false;
|
||||
}
|
||||
|
||||
int rpng_process_image(rpng_t *rpng,
|
||||
@ -1170,9 +1200,6 @@ void rpng_free(rpng_t *rpng)
|
||||
|
||||
bool rpng_start(rpng_t *rpng)
|
||||
{
|
||||
unsigned i;
|
||||
char header[8];
|
||||
|
||||
if (!rpng)
|
||||
return false;
|
||||
|
||||
@ -1181,12 +1208,8 @@ bool rpng_start(rpng_t *rpng)
|
||||
if (rpng->buff_end - rpng->buff_data < 8)
|
||||
return false;
|
||||
|
||||
header[0] = '\0';
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
header[i] = rpng->buff_data[i];
|
||||
|
||||
if (string_is_not_equal_fast(header, png_magic, sizeof(png_magic)))
|
||||
if (string_is_not_equal_fast(
|
||||
rpng->buff_data, png_magic, sizeof(png_magic)))
|
||||
return false;
|
||||
|
||||
rpng->buff_data += 8;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rpng_encode.c).
|
||||
@ -36,7 +36,7 @@
|
||||
fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); \
|
||||
ret = false; \
|
||||
goto end; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
double DEFLATE_PADDING = 1.1;
|
||||
int PNG_ROUGH_HEADER = 100;
|
||||
@ -413,7 +413,7 @@ uint8_t* rpng_save_image_bgr24_string(const uint8_t *data,
|
||||
buf = (uint8_t*)malloc(buf_length*sizeof(uint8_t));
|
||||
if (!buf)
|
||||
GOTO_END_ERROR();
|
||||
|
||||
|
||||
intf_s = intfstream_open_writable_memory(buf,
|
||||
RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE,
|
||||
@ -424,7 +424,7 @@ uint8_t* rpng_save_image_bgr24_string(const uint8_t *data,
|
||||
|
||||
*bytes = intfstream_get_ptr(intf_s);
|
||||
intfstream_rewind(intf_s);
|
||||
output = (uint8_t*)malloc((*bytes)*sizeof(uint8_t));
|
||||
output = (uint8_t*)malloc((size_t)((*bytes)*sizeof(uint8_t)));
|
||||
if (!output)
|
||||
GOTO_END_ERROR();
|
||||
intfstream_read(intf_s, output, *bytes);
|
||||
@ -433,7 +433,10 @@ end:
|
||||
if (buf)
|
||||
free(buf);
|
||||
if (intf_s)
|
||||
{
|
||||
intfstream_close(intf_s);
|
||||
free(intf_s);
|
||||
}
|
||||
if (ret == false)
|
||||
{
|
||||
if (output)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rpng_internal.h).
|
||||
@ -27,13 +27,6 @@
|
||||
#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
|
||||
|
61
externals/libretro-common/formats/tga/rtga.c
vendored
61
externals/libretro-common/formats/tga/rtga.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rtga.c).
|
||||
@ -45,15 +45,13 @@ struct rtga
|
||||
|
||||
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;
|
||||
int buflen;
|
||||
int img_n, img_out_n;
|
||||
uint32_t img_x, img_y;
|
||||
uint8_t buffer_start[128];
|
||||
} rtga_context;
|
||||
|
||||
static INLINE uint8_t rtga_get8(rtga_context *s)
|
||||
@ -102,40 +100,40 @@ static unsigned char *rtga_convert_format(
|
||||
switch (((img_n)*8+(req_comp)))
|
||||
{
|
||||
case ((1)*8+(2)):
|
||||
for(i=x-1; i >= 0; --i, src += 1, dest += 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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
for (i=x-1; i >= 0; --i, src += 3, dest += 4)
|
||||
{
|
||||
dest[0]=src[0];
|
||||
dest[1]=src[1];
|
||||
@ -144,29 +142,29 @@ static unsigned char *rtga_convert_format(
|
||||
}
|
||||
break;
|
||||
case ((3)*8+(1)):
|
||||
for(i=x-1; i >= 0; --i, src += 3, dest += 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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
for (i=x-1; i >= 0; --i, src += 4, dest += 3)
|
||||
{
|
||||
dest[0]=src[0];
|
||||
dest[1]=src[1];
|
||||
@ -357,18 +355,21 @@ static uint8_t *rtga_tga_load(rtga_context *s,
|
||||
/* do I need to invert the image? */
|
||||
if (tga_inverted)
|
||||
{
|
||||
for (j = 0; j*2 < tga_height; ++j)
|
||||
if (tga_data)
|
||||
{
|
||||
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)
|
||||
for (j = 0; j*2 < tga_height; ++j)
|
||||
{
|
||||
unsigned char temp = tga_data[index1];
|
||||
tga_data[index1] = tga_data[index2];
|
||||
tga_data[index2] = temp;
|
||||
++index1;
|
||||
++index2;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -431,7 +432,7 @@ int rtga_process_image(rtga_t *rtga, void **buf_data,
|
||||
size_tex = (*width) * (*height);
|
||||
|
||||
/* Convert RGBA to ARGB */
|
||||
while(size_tex--)
|
||||
while (size_tex--)
|
||||
{
|
||||
unsigned int texel = rtga->output_image[size_tex];
|
||||
unsigned int A = texel & 0xFF000000;
|
||||
|
7
externals/libretro-common/formats/wav/rwav.c
vendored
7
externals/libretro-common/formats/wav/rwav.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rwav.c).
|
||||
@ -95,12 +95,13 @@ enum rwav_state rwav_iterate(rwav_iterator_t *iter)
|
||||
|
||||
rwav->subchunk2size = data[40] | data[41] << 8 | data[42] << 16 | data[43] << 24;
|
||||
|
||||
if (rwav->subchunk2size > iter->size - 44)
|
||||
if ((rwav->subchunk2size < 1) ||
|
||||
(rwav->subchunk2size > iter->size - 44))
|
||||
return RWAV_ITERATE_ERROR; /* too few bytes in buffer */
|
||||
|
||||
samples = malloc(rwav->subchunk2size);
|
||||
|
||||
if (samples == NULL)
|
||||
if (!samples)
|
||||
return RWAV_ITERATE_ERROR;
|
||||
|
||||
rwav->numchannels = data[22] | data[23] << 8;
|
||||
|
584
externals/libretro-common/formats/xml/rxml.c
vendored
584
externals/libretro-common/formats/xml/rxml.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rxml.c).
|
||||
@ -20,14 +20,6 @@
|
||||
* 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>
|
||||
@ -35,6 +27,17 @@
|
||||
|
||||
#include <formats/rxml.h>
|
||||
|
||||
#include "../../deps/yxml/yxml.h"
|
||||
|
||||
#define BUFSIZE 4096
|
||||
|
||||
struct rxml_parse_buffer
|
||||
{
|
||||
rxml_node_t *stack[32];
|
||||
char xml[BUFSIZE];
|
||||
char val[BUFSIZE];
|
||||
};
|
||||
|
||||
struct rxml_document
|
||||
{
|
||||
struct rxml_node *root_node;
|
||||
@ -64,19 +67,15 @@ static void rxml_free_node(struct rxml_node *node)
|
||||
|
||||
for (attrib_node_head = node->attrib; attrib_node_head; )
|
||||
{
|
||||
struct rxml_attrib_node *next_attrib = NULL;
|
||||
struct rxml_attrib_node *next_attrib =
|
||||
(struct rxml_attrib_node*)attrib_node_head->next;
|
||||
|
||||
next_attrib = (struct rxml_attrib_node*)attrib_node_head->next;
|
||||
|
||||
if (next_attrib)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -89,362 +88,30 @@ static void rxml_free_node(struct rxml_node *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 rxml_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)
|
||||
{
|
||||
const char *elem;
|
||||
struct rxml_attrib_node *list = NULL;
|
||||
struct rxml_attrib_node *tail = NULL;
|
||||
char *attrib = NULL;
|
||||
char *value = NULL;
|
||||
char *last_char = NULL;
|
||||
char *save = NULL;
|
||||
char *copy = strdup(str);
|
||||
if (!copy)
|
||||
return NULL;
|
||||
|
||||
last_char = copy + strlen(copy) - 1;
|
||||
if (*last_char == '/')
|
||||
*last_char = '\0';
|
||||
|
||||
elem = strtok_r(copy, " \n\t\f\v\r", &save);
|
||||
while (elem)
|
||||
{
|
||||
const char *end;
|
||||
struct rxml_attrib_node *new_node;
|
||||
const char *eq = strstr(elem, "=\"");
|
||||
if (!eq)
|
||||
goto end;
|
||||
|
||||
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;
|
||||
|
||||
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 *name_end;
|
||||
const char *str_ptr = str;
|
||||
rxml_skip_spaces(&str_ptr);
|
||||
|
||||
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;
|
||||
|
||||
rxml_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 copied = 0;
|
||||
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;
|
||||
|
||||
closing_tag[copied] = '<';
|
||||
closing_tag[copied+1] = '/';
|
||||
closing_tag[copied+2] = '\0';
|
||||
|
||||
copied = strlcat(closing_tag, node->name, closing_tag_size);
|
||||
|
||||
closing_tag[copied] = '>';
|
||||
closing_tag[copied+1] = '\0';
|
||||
|
||||
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_CONST("<![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)
|
||||
{
|
||||
char *copy_dest;
|
||||
const char *copy_src;
|
||||
size_t len = strlen(str);
|
||||
char *new_str = (char*)malloc(len + 1);
|
||||
if (!new_str)
|
||||
return NULL;
|
||||
|
||||
new_str[len] = '\0';
|
||||
|
||||
copy_dest = new_str;
|
||||
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_CONST("-->");
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
rxml_document_t *doc;
|
||||
rxml_document_t *doc = NULL;
|
||||
char *memory_buffer = NULL;
|
||||
long len = 0;
|
||||
int64_t len = 0;
|
||||
RFILE *file = filestream_open(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
len = filestream_get_size(file);
|
||||
memory_buffer = (char*)malloc(len + 1);
|
||||
len = filestream_get_size(file);
|
||||
memory_buffer = (char*)malloc((size_t)(len + 1));
|
||||
if (!memory_buffer)
|
||||
goto error;
|
||||
|
||||
memory_buffer[len] = '\0';
|
||||
memory_buffer[len] = '\0';
|
||||
if (filestream_read(file, memory_buffer, len) != (size_t)len)
|
||||
goto error;
|
||||
|
||||
filestream_close(file);
|
||||
file = NULL;
|
||||
file = NULL;
|
||||
|
||||
doc = rxml_load_document_string(memory_buffer);
|
||||
doc = rxml_load_document_string(memory_buffer);
|
||||
|
||||
free(memory_buffer);
|
||||
return doc;
|
||||
@ -458,35 +125,200 @@ error:
|
||||
|
||||
rxml_document_t *rxml_load_document_string(const char *str)
|
||||
{
|
||||
rxml_document_t *doc;
|
||||
char *memory_buffer = NULL;
|
||||
const char *mem_ptr = NULL;
|
||||
yxml_t x;
|
||||
rxml_document_t *doc = NULL;
|
||||
size_t stack_i = 0;
|
||||
size_t level = 0;
|
||||
int c = 0;
|
||||
char *valptr = NULL;
|
||||
rxml_node_t *node = NULL;
|
||||
struct rxml_attrib_node *attr = NULL;
|
||||
struct rxml_parse_buffer *buf = (struct rxml_parse_buffer*)
|
||||
malloc(sizeof(*buf));
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
doc = (rxml_document_t*)calloc(1, sizeof(*doc));
|
||||
valptr = buf->val;
|
||||
doc = (rxml_document_t*)malloc(sizeof(*doc));
|
||||
if (!doc)
|
||||
goto error;
|
||||
|
||||
mem_ptr = str;
|
||||
doc->root_node = (struct rxml_node *)malloc(
|
||||
sizeof(*doc->root_node));
|
||||
|
||||
doc->root_node->name = NULL;
|
||||
doc->root_node->data = NULL;
|
||||
doc->root_node->attrib = NULL;
|
||||
|
||||
if (!validate_header(&mem_ptr))
|
||||
goto error;
|
||||
doc->root_node->children = NULL;
|
||||
doc->root_node->next = NULL;
|
||||
|
||||
memory_buffer = purge_xml_comments(mem_ptr);
|
||||
if (!memory_buffer)
|
||||
goto error;
|
||||
yxml_init(&x, buf->xml, BUFSIZE);
|
||||
|
||||
mem_ptr = memory_buffer;
|
||||
for (; *str; ++str)
|
||||
{
|
||||
yxml_ret_t r = yxml_parse(&x, *str);
|
||||
|
||||
doc->root_node = rxml_parse_node(&mem_ptr);
|
||||
if (!doc->root_node)
|
||||
goto error;
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
free(memory_buffer);
|
||||
switch (r)
|
||||
{
|
||||
|
||||
case YXML_ELEMSTART:
|
||||
if (node)
|
||||
{
|
||||
if (level > stack_i)
|
||||
{
|
||||
buf->stack[stack_i] = node;
|
||||
++stack_i;
|
||||
|
||||
node->children = (rxml_node_t*)
|
||||
malloc(sizeof(*node));
|
||||
|
||||
node->children->name = NULL;
|
||||
node->children->data = NULL;
|
||||
node->children->attrib = NULL;
|
||||
node->children->children = NULL;
|
||||
node->children->next = NULL;
|
||||
|
||||
node = node->children;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->next = (rxml_node_t*)
|
||||
malloc(sizeof(*node));
|
||||
|
||||
node->next->name = NULL;
|
||||
node->next->data = NULL;
|
||||
node->next->attrib = NULL;
|
||||
node->next->children = NULL;
|
||||
node->next->next = NULL;
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
node = doc->root_node = (rxml_node_t*)
|
||||
calloc(1, sizeof(*node));
|
||||
|
||||
if (node->name)
|
||||
free(node->name);
|
||||
node->name = strdup(x.elem);
|
||||
|
||||
attr = NULL;
|
||||
|
||||
++level;
|
||||
break;
|
||||
|
||||
case YXML_ELEMEND:
|
||||
--level;
|
||||
|
||||
if (valptr > buf->val)
|
||||
{
|
||||
*valptr = '\0';
|
||||
|
||||
/* Original code was broken here:
|
||||
* > If an element ended on two successive
|
||||
* iterations, on the second iteration
|
||||
* the 'data' for the *previous* node would
|
||||
* get overwritten
|
||||
* > This effectively erased the data for the
|
||||
* previous node, *and* caused a memory leak
|
||||
* (due to the double strdup())
|
||||
* It seems the correct thing to do here is
|
||||
* only copy the data if the current 'level'
|
||||
* and 'stack index' are the same... */
|
||||
if (level == stack_i)
|
||||
{
|
||||
if (node->data)
|
||||
free(node->data);
|
||||
node->data = strdup(buf->val);
|
||||
}
|
||||
|
||||
valptr = buf->val;
|
||||
}
|
||||
|
||||
if (level < stack_i)
|
||||
{
|
||||
--stack_i;
|
||||
node = buf->stack[stack_i];
|
||||
}
|
||||
break;
|
||||
|
||||
case YXML_CONTENT:
|
||||
for (c = 0; c < sizeof(x.data) && x.data[c]; ++c)
|
||||
{
|
||||
*valptr = x.data[c];
|
||||
++valptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case YXML_ATTRSTART:
|
||||
if (attr)
|
||||
{
|
||||
struct rxml_attrib_node
|
||||
*new_node = (struct rxml_attrib_node*)
|
||||
calloc(1, sizeof(*attr));
|
||||
attr = new_node;
|
||||
attr->next = new_node ;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct rxml_attrib_node
|
||||
*new_node = (struct rxml_attrib_node*)
|
||||
calloc(1, sizeof(*attr));
|
||||
attr = new_node;
|
||||
|
||||
if (node)
|
||||
node->attrib = new_node;
|
||||
}
|
||||
|
||||
if (attr)
|
||||
{
|
||||
if (attr->attrib)
|
||||
free(attr->attrib);
|
||||
attr->attrib = strdup(x.attr);
|
||||
}
|
||||
|
||||
valptr = buf->val;
|
||||
break;
|
||||
|
||||
case YXML_ATTRVAL:
|
||||
for (c = 0; c < sizeof(x.data) && x.data[c]; ++c)
|
||||
{
|
||||
*valptr = x.data[c];
|
||||
++valptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case YXML_ATTREND:
|
||||
if (valptr > buf->val)
|
||||
{
|
||||
*valptr = '\0';
|
||||
|
||||
if (attr)
|
||||
{
|
||||
if (attr->value)
|
||||
free(attr->value);
|
||||
attr->value = strdup(buf->val);
|
||||
}
|
||||
|
||||
valptr = buf->val;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return doc;
|
||||
|
||||
error:
|
||||
free(memory_buffer);
|
||||
rxml_free_document(doc);
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,12 @@ TARGET := rxml
|
||||
|
||||
LIBRETRO_XML_DIR := ..
|
||||
LIBRETRO_COMM_DIR := ../../..
|
||||
LIBRETRO_DEPS_DIR := ../../../../deps
|
||||
|
||||
SOURCES := \
|
||||
rxml_test.c \
|
||||
$(LIBRETRO_XML_DIR)/rxml.c \
|
||||
$(LIBRETRO_DEPS_DIR)/yxml/yxml.c \
|
||||
$(LIBRETRO_COMM_DIR)/streams/file_stream.c
|
||||
|
||||
OBJS := $(SOURCES:.c=.o)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (gl_capabilities.c).
|
||||
@ -300,7 +300,7 @@ bool gl_check_capability(enum gl_capability_enum enum_idx)
|
||||
case GL_CAPS_BGRA8888:
|
||||
#ifdef HAVE_OPENGLES
|
||||
/* There are both APPLE and EXT variants. */
|
||||
if (gl_query_extension("BGRA8888") && !strstr(renderer, "VideoCore"))
|
||||
if (gl_query_extension("BGRA8888"))
|
||||
return true;
|
||||
#else
|
||||
return true;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (pixconv.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (scaler.c).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (scaler_filter.c).
|
||||
@ -85,7 +85,9 @@ static bool validate_filter(struct scaler_ctx *ctx)
|
||||
{
|
||||
if (ctx->horiz.filter_pos[i] > max_w_pos || ctx->horiz.filter_pos[i] < 0)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
fprintf(stderr, "Out X = %d => In X = %d\n", i, ctx->horiz.filter_pos[i]);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -96,7 +98,9 @@ static bool validate_filter(struct scaler_ctx *ctx)
|
||||
{
|
||||
if (ctx->vert.filter_pos[i] > max_h_pos || ctx->vert.filter_pos[i] < 0)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
fprintf(stderr, "Out Y = %d => In Y = %d\n", i, ctx->vert.filter_pos[i]);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (scaler_int.c).
|
||||
|
2
externals/libretro-common/glsym/glgen.py
vendored
Normal file → Executable file
2
externals/libretro-common/glsym/glgen.py
vendored
Normal file → Executable file
@ -2,9 +2,7 @@
|
||||
|
||||
"""
|
||||
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
|
||||
|
2
externals/libretro-common/glsym/glsym_gl.c
vendored
2
externals/libretro-common/glsym/glsym_gl.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this libretro SDK code part (glsym).
|
||||
|
2
externals/libretro-common/glsym/rglgen.c
vendored
2
externals/libretro-common/glsym/rglgen.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this libretro SDK code part (glsym).
|
||||
|
2
externals/libretro-common/glsym/rglgen.py
vendored
Normal file → Executable file
2
externals/libretro-common/glsym/rglgen.py
vendored
Normal file → Executable file
@ -2,9 +2,7 @@
|
||||
|
||||
"""
|
||||
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
|
||||
|
2
externals/libretro-common/glsym/xglgen.py
vendored
2
externals/libretro-common/glsym/xglgen.py
vendored
@ -2,9 +2,7 @@
|
||||
|
||||
"""
|
||||
License statement applies to this file (xglgen.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
|
||||
|
49
externals/libretro-common/hash/rhash.c
vendored
49
externals/libretro-common/hash/rhash.c
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rhash.c).
|
||||
@ -307,7 +307,22 @@ uint32_t crc32_calculate(const uint8_t *data, size_t length)
|
||||
/* Define the circular shift macro */
|
||||
#define SHA1CircularShift(bits,word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32-(bits))))
|
||||
|
||||
static void SHA1Reset(SHA1Context *context)
|
||||
struct sha1_context
|
||||
{
|
||||
unsigned Message_Digest[5]; /* Message Digest (output) */
|
||||
|
||||
unsigned Length_Low; /* Message length in bits */
|
||||
unsigned Length_High; /* Message length in bits */
|
||||
|
||||
unsigned char Message_Block[64]; /* 512-bit message blocks */
|
||||
int Message_Block_Index; /* Index into message block array */
|
||||
|
||||
int Computed; /* Is the digest computed? */
|
||||
int Corrupted; /* Is the message digest corruped? */
|
||||
};
|
||||
|
||||
|
||||
static void SHA1Reset(struct sha1_context *context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
@ -326,7 +341,7 @@ static void SHA1Reset(SHA1Context *context)
|
||||
context->Corrupted = 0;
|
||||
}
|
||||
|
||||
static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
static void SHA1ProcessMessageBlock(struct sha1_context *context)
|
||||
{
|
||||
const unsigned K[] = /* Constants defined in SHA-1 */
|
||||
{
|
||||
@ -341,7 +356,7 @@ static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
unsigned A, B, C, D, E; /* Word buffers */
|
||||
|
||||
/* Initialize the first 16 words in the array W */
|
||||
for(t = 0; t < 16; t++)
|
||||
for (t = 0; t < 16; t++)
|
||||
{
|
||||
W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
|
||||
W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
|
||||
@ -349,7 +364,7 @@ static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
|
||||
}
|
||||
|
||||
for(t = 16; t < 80; t++)
|
||||
for (t = 16; t < 80; t++)
|
||||
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
||||
|
||||
A = context->Message_Digest[0];
|
||||
@ -358,7 +373,7 @@ static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
D = context->Message_Digest[3];
|
||||
E = context->Message_Digest[4];
|
||||
|
||||
for(t = 0; t < 20; t++)
|
||||
for (t = 0; t < 20; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | ((~B) & D)) + E + W[t] + K[0];
|
||||
@ -370,7 +385,7 @@ static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 20; t < 40; t++)
|
||||
for (t = 20; t < 40; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
|
||||
temp &= 0xFFFFFFFF;
|
||||
@ -381,7 +396,7 @@ static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 40; t < 60; t++)
|
||||
for (t = 40; t < 60; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
|
||||
@ -393,7 +408,7 @@ static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 60; t < 80; t++)
|
||||
for (t = 60; t < 80; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
|
||||
temp &= 0xFFFFFFFF;
|
||||
@ -418,7 +433,7 @@ static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
context->Message_Block_Index = 0;
|
||||
}
|
||||
|
||||
static void SHA1PadMessage(SHA1Context *context)
|
||||
static void SHA1PadMessage(struct sha1_context *context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
@ -433,13 +448,13 @@ static void SHA1PadMessage(SHA1Context *context)
|
||||
|
||||
if (context->Message_Block_Index > 55)
|
||||
{
|
||||
while(context->Message_Block_Index < 64)
|
||||
while (context->Message_Block_Index < 64)
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
|
||||
while(context->Message_Block_Index < 56)
|
||||
while (context->Message_Block_Index < 56)
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
|
||||
/* Store the message length as the last 8 octets */
|
||||
@ -455,7 +470,7 @@ static void SHA1PadMessage(SHA1Context *context)
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
|
||||
static int SHA1Result(SHA1Context *context)
|
||||
static int SHA1Result(struct sha1_context *context)
|
||||
{
|
||||
if (context->Corrupted)
|
||||
return 0;
|
||||
@ -469,7 +484,7 @@ static int SHA1Result(SHA1Context *context)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void SHA1Input(SHA1Context *context,
|
||||
static void SHA1Input(struct sha1_context *context,
|
||||
const unsigned char *message_array,
|
||||
unsigned length)
|
||||
{
|
||||
@ -482,7 +497,7 @@ static void SHA1Input(SHA1Context *context,
|
||||
return;
|
||||
}
|
||||
|
||||
while(length-- && !context->Corrupted)
|
||||
while (length-- && !context->Corrupted)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] =
|
||||
(*message_array & 0xFF);
|
||||
@ -508,7 +523,7 @@ static void SHA1Input(SHA1Context *context,
|
||||
|
||||
int sha1_calculate(const char *path, char *result)
|
||||
{
|
||||
SHA1Context sha;
|
||||
struct sha1_context sha;
|
||||
unsigned char buff[4096];
|
||||
int rv = 1;
|
||||
RFILE *fd = filestream_open(path,
|
||||
@ -529,7 +544,7 @@ int sha1_calculate(const char *path, char *result)
|
||||
goto error;
|
||||
|
||||
SHA1Input(&sha, buff, rv);
|
||||
}while(rv);
|
||||
} while (rv);
|
||||
|
||||
if (!SHA1Result(&sha))
|
||||
goto error;
|
||||
|
1003
externals/libretro-common/include/array/dynarray.h
vendored
1003
externals/libretro-common/include/array/dynarray.h
vendored
File diff suppressed because it is too large
Load Diff
111
externals/libretro-common/include/array/rbuf.h
vendored
Normal file
111
externals/libretro-common/include/array/rbuf.h
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rbuf.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __LIBRETRO_SDK_ARRAY_RBUF_H__
|
||||
#define __LIBRETRO_SDK_ARRAY_RBUF_H__
|
||||
|
||||
/*
|
||||
* This file implements stretchy buffers as invented (?) by Sean Barrett.
|
||||
* Based on the implementation from the public domain Bitwise project
|
||||
* by Per Vognsen - https://github.com/pervognsen/bitwise
|
||||
*
|
||||
* It's a super simple type safe dynamic array for C with no need
|
||||
* to predeclare any type or anything.
|
||||
* The first time an element is added, memory for 16 elements are allocated.
|
||||
* Then every time length is about to exceed capacity, capacity is doubled.
|
||||
*
|
||||
* Be careful not to supply modifying statements to the macro arguments.
|
||||
* Something like RBUF_REMOVE(buf, i--); would have unintended results.
|
||||
*
|
||||
* Sample usage:
|
||||
*
|
||||
* mytype_t* buf = NULL;
|
||||
* RBUF_PUSH(buf, some_element);
|
||||
* RBUF_PUSH(buf, other_element);
|
||||
* -- now RBUF_LEN(buf) == 2, buf[0] == some_element, buf[1] == other_element
|
||||
*
|
||||
* -- Free allocated memory:
|
||||
* RBUF_FREE(buf);
|
||||
* -- now buf == NULL, RBUF_LEN(buf) == 0, RBUF_CAP(buf) == 0
|
||||
*
|
||||
* -- Explicitly increase allocated memory and set capacity:
|
||||
* RBUF_FIT(buf, 100);
|
||||
* -- now RBUF_LEN(buf) == 0, RBUF_CAP(buf) == 100
|
||||
*
|
||||
* -- Resize buffer (does not initialize or zero memory!)
|
||||
* RBUF_RESIZE(buf, 200);
|
||||
* -- now RBUF_LEN(buf) == 200, RBUF_CAP(buf) == 200
|
||||
*
|
||||
* -- To handle running out of memory:
|
||||
* bool ran_out_of_memory = !RBUF_TRYFIT(buf, 1000);
|
||||
* -- before RESIZE or PUSH. When out of memory, buf will stay unmodified.
|
||||
*/
|
||||
|
||||
#include <retro_math.h> /* for MAX */
|
||||
#include <stdlib.h> /* for malloc, realloc */
|
||||
|
||||
#define RBUF__HDR(b) (((struct rbuf__hdr *)(b))-1)
|
||||
|
||||
#define RBUF_LEN(b) ((b) ? RBUF__HDR(b)->len : 0)
|
||||
#define RBUF_CAP(b) ((b) ? RBUF__HDR(b)->cap : 0)
|
||||
#define RBUF_END(b) ((b) + RBUF_LEN(b))
|
||||
#define RBUF_SIZEOF(b) ((b) ? RBUF_LEN(b)*sizeof(*b) : 0)
|
||||
|
||||
#define RBUF_FREE(b) ((b) ? (free(RBUF__HDR(b)), (b) = NULL) : 0)
|
||||
#define RBUF_FIT(b, n) ((size_t)(n) <= RBUF_CAP(b) ? 0 : (*(void**)(&(b)) = rbuf__grow((b), (n), sizeof(*(b)))))
|
||||
#define RBUF_PUSH(b, val) (RBUF_FIT((b), 1 + RBUF_LEN(b)), (b)[RBUF__HDR(b)->len++] = (val))
|
||||
#define RBUF_POP(b) (b)[--RBUF__HDR(b)->len]
|
||||
#define RBUF_RESIZE(b, sz) (RBUF_FIT((b), (sz)), ((b) ? RBUF__HDR(b)->len = (sz) : 0))
|
||||
#define RBUF_CLEAR(b) ((b) ? RBUF__HDR(b)->len = 0 : 0)
|
||||
#define RBUF_TRYFIT(b, n) (RBUF_FIT((b), (n)), (((b) && RBUF_CAP(b) >= (size_t)(n)) || !(n)))
|
||||
#define RBUF_REMOVE(b, idx) memmove((b) + (idx), (b) + (idx) + 1, (--RBUF__HDR(b)->len - (idx)) * sizeof(*(b)))
|
||||
|
||||
struct rbuf__hdr
|
||||
{
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
static void *rbuf__grow(void *buf,
|
||||
size_t new_len, size_t elem_size)
|
||||
{
|
||||
struct rbuf__hdr *new_hdr;
|
||||
size_t new_cap = MAX(2 * RBUF_CAP(buf), MAX(new_len, 16));
|
||||
size_t new_size = sizeof(struct rbuf__hdr) + new_cap*elem_size;
|
||||
if (buf)
|
||||
{
|
||||
new_hdr = (struct rbuf__hdr *)realloc(RBUF__HDR(buf), new_size);
|
||||
if (!new_hdr)
|
||||
return buf; /* out of memory, return unchanged */
|
||||
}
|
||||
else
|
||||
{
|
||||
new_hdr = (struct rbuf__hdr *)malloc(new_size);
|
||||
if (!new_hdr)
|
||||
return NULL; /* out of memory */
|
||||
new_hdr->len = 0;
|
||||
}
|
||||
new_hdr->cap = new_cap;
|
||||
return new_hdr + 1;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_mix.h).
|
||||
@ -40,19 +40,19 @@ RETRO_BEGIN_DECLS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double ratio;
|
||||
void *buf;
|
||||
int16_t *upsample_buf;
|
||||
float *float_buf;
|
||||
float *float_resample_buf;
|
||||
int16_t *resample_buf;
|
||||
ssize_t len;
|
||||
size_t resample_len;
|
||||
rwav_t *rwav;
|
||||
int sample_rate;
|
||||
bool resample;
|
||||
const retro_resampler_t *resampler;
|
||||
void *resampler_data;
|
||||
double ratio;
|
||||
rwav_t *rwav;
|
||||
ssize_t len;
|
||||
size_t resample_len;
|
||||
int sample_rate;
|
||||
bool resample;
|
||||
} audio_chunk_t;
|
||||
|
||||
#if defined(__SSE2__)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_mixer.h).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_resampler.h).
|
||||
@ -155,7 +155,6 @@ extern retro_resampler_t sinc_resampler;
|
||||
extern retro_resampler_t CC_resampler;
|
||||
#endif
|
||||
extern retro_resampler_t nearest_resampler;
|
||||
extern retro_resampler_t null_resampler;
|
||||
|
||||
/**
|
||||
* audio_resampler_driver_find_handle:
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (float_to_s16.h).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (s16_to_float.h).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (dsp_filter.h).
|
||||
|
2
externals/libretro-common/include/boolean.h
vendored
2
externals/libretro-common/include/boolean.h
vendored
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2018 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (boolean.h).
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2019 The RetroArch team
|
||||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (cdrom.h).
|
||||
@ -62,10 +62,10 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char drive;
|
||||
cdrom_track_t track[99]; /* unsigned alignment */
|
||||
cdrom_group_timeouts_t timeouts; /* unsigned short alignment */
|
||||
unsigned char num_tracks;
|
||||
cdrom_group_timeouts_t timeouts;
|
||||
cdrom_track_t track[99];
|
||||
char drive;
|
||||
} cdrom_toc_t;
|
||||
|
||||
void cdrom_lba_to_msf(unsigned lba, unsigned char *min, unsigned char *sec, unsigned char *frame);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user