12 Commits

Author SHA1 Message Date
Ray
8743e11285 Update rmodels.c 2026-03-29 21:40:41 +02:00
Ray
04f81538b7 ADDED: Some sample code to export gltf/glb meshes -WIP- 2026-03-29 21:37:01 +02:00
Ray
8d70f1007b Update CHANGELOG 2026-03-29 21:24:33 +02:00
Ray
6c0134bb5c Create cgltf_write.h 2026-03-29 21:24:24 +02:00
Ray
e9231bc4f1 Update stb_image_resize2.h 2026-03-29 21:24:20 +02:00
Ray
4c79c6837b Update qoi.h 2026-03-29 21:21:16 +02:00
Ray
e5f0c9f8b1 Update qoa.h 2026-03-29 21:21:11 +02:00
Ray
d5326fe880 Update m3d.h 2026-03-29 21:20:23 +02:00
Ray
adc4c9b875 Update dr_wav.h 2026-03-29 21:19:31 +02:00
Ray
b09da8fce8 Update dr_mp3.h 2026-03-29 21:18:56 +02:00
Ray
0b87a35e5a Update dr_flac.h 2026-03-29 21:18:28 +02:00
Ray
a1bf8d9c75 Update cgltf.h 2026-03-29 21:17:51 +02:00
11 changed files with 2637 additions and 575 deletions

View File

@ -759,6 +759,7 @@ Detailed changes:
[rexm] REVIEWED: `Makefile.Web` before trying to rebuild new example for web by @raysan5
[rexm] REVIEWED: Using `Makefile.Web` for specific web versions generation by @raysan5
[external] ADDED: `cgltf_write` 1.15, to support glTF models export in the future, by @raysan5
[external] RENAMED: `rl_gputex.h` to `rltexgpu.h`, compressed textures loading
[external] REVIEWED: `rltexgpu.h`, make it usable standalone by @raysan5
[external] REVIEWED: `rltexgpu.h, fix the swizzling in `rl_load_dds_from_memory()` (#5422) by @msmith-codes
@ -767,16 +768,23 @@ Detailed changes:
[external] REVIEWED: `sdefl` and `sinfl` issues (#5367) by @raysan5
[external] REVIEWED: `sinfl_bsr()`, improvements by @RicoP
[external] REVIEWED: `stb_truetype`, fix composite glyph scaling logic (#4811) by @ashishbhattarai
[external] UPDATED: `raygui` to 5.0-dev for examples by @raysan5
[external] UPDATED: dr_libs (#5020) by @Emil2010
[external] UPDATED: miniaudio to v0.11.22 (#4983) by @M374LX
[external] UPDATED: miniaudio to v0.11.23 (#5234) by @pyrokn8
[external] UPDATED: miniaudio to v0.11.24 (#5506) by @vdemcak
[external] UPDATED: raygui to 5.0-dev for examples by @raysan5
[external] UPDATED: RGFW to 1.5 (#4688) by @ColleagueRiley
[external] UPDATED: RGFW to 1.6 (#4795) by @colleagueRiley
[external] UPDATED: RGFW to 1.7 (#4965) by @M374LX
[external] UPDATED: RGFW to 1.7.5-dev (#4976) by @M374LX
[external] UPDATED: RGFW to 2.0.0 (#5582) by @CrackedPixel
[external] UPDATED: `miniaudio` to v0.11.22 (#4983) by @M374LX
[external] UPDATED: `miniaudio` to v0.11.23 (#5234) by @pyrokn8
[external] UPDATED: `miniaudio` to v0.11.24 (#5506) by @vdemcak
[external] UPDATED: `RGFW` to 1.5 (#4688) by @ColleagueRiley
[external] UPDATED: `RGFW` to 1.6 (#4795) by @colleagueRiley
[external] UPDATED: `RGFW` to 1.7 (#4965) by @M374LX
[external] UPDATED: `RGFW` to 1.7.5-dev (#4976) by @M374LX
[external] UPDATED: `RGFW` to 2.0.0 (#5582) by @CrackedPixel
[external] UPDATED: `cgltf` 1.14 to 1.15 by @raysan5
[external] UPDATED: `dr_flac` v0.13.0 to v0.13.3 by @raysan5
[external] UPDATED: `dr_mp3` v0.7.0 to v0.7.4 by @raysan5
[external] UPDATED: `m3d` to latest master by @raysan5
[external] UPDATED: `qoi` to latest master by @raysan5
[external] UPDATED: `qoa` to latest master by @raysan5
[external] UPDATED: `stb_image_resize2` v2.12 to v2.18 by @raysan5
[misc] ADDED: SECURITY.md for security reporting policies by @raysan5
[misc] ADDED: `examples/examples_list`, to be used by `rexm` or other tools by @raysan5

211
src/external/cgltf.h vendored
View File

@ -1,7 +1,7 @@
/**
* cgltf - a single-file glTF 2.0 parser written in C99.
*
* Version: 1.14
* Version: 1.15
*
* Website: https://github.com/jkuhlmann/cgltf
*
@ -141,7 +141,7 @@ typedef struct cgltf_memory_options
typedef struct cgltf_file_options
{
cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data);
void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data);
void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data, cgltf_size size);
void* user_data;
} cgltf_file_options;
@ -296,6 +296,7 @@ typedef enum cgltf_meshopt_compression_filter {
cgltf_meshopt_compression_filter_octahedral,
cgltf_meshopt_compression_filter_quaternion,
cgltf_meshopt_compression_filter_exponential,
cgltf_meshopt_compression_filter_color,
cgltf_meshopt_compression_filter_max_enum
} cgltf_meshopt_compression_filter;
@ -308,6 +309,7 @@ typedef struct cgltf_meshopt_compression
cgltf_size count;
cgltf_meshopt_compression_mode mode;
cgltf_meshopt_compression_filter filter;
cgltf_bool is_khr;
} cgltf_meshopt_compression;
typedef struct cgltf_buffer_view
@ -376,13 +378,29 @@ typedef struct cgltf_image
cgltf_extension* extensions;
} cgltf_image;
typedef enum cgltf_filter_type {
cgltf_filter_type_undefined = 0,
cgltf_filter_type_nearest = 9728,
cgltf_filter_type_linear = 9729,
cgltf_filter_type_nearest_mipmap_nearest = 9984,
cgltf_filter_type_linear_mipmap_nearest = 9985,
cgltf_filter_type_nearest_mipmap_linear = 9986,
cgltf_filter_type_linear_mipmap_linear = 9987
} cgltf_filter_type;
typedef enum cgltf_wrap_mode {
cgltf_wrap_mode_clamp_to_edge = 33071,
cgltf_wrap_mode_mirrored_repeat = 33648,
cgltf_wrap_mode_repeat = 10497
} cgltf_wrap_mode;
typedef struct cgltf_sampler
{
char* name;
cgltf_int mag_filter;
cgltf_int min_filter;
cgltf_int wrap_s;
cgltf_int wrap_t;
cgltf_filter_type mag_filter;
cgltf_filter_type min_filter;
cgltf_wrap_mode wrap_s;
cgltf_wrap_mode wrap_t;
cgltf_extras extras;
cgltf_size extensions_count;
cgltf_extension* extensions;
@ -500,6 +518,14 @@ typedef struct cgltf_iridescence
cgltf_texture_view iridescence_thickness_texture;
} cgltf_iridescence;
typedef struct cgltf_diffuse_transmission
{
cgltf_texture_view diffuse_transmission_texture;
cgltf_float diffuse_transmission_factor;
cgltf_float diffuse_transmission_color_factor[3];
cgltf_texture_view diffuse_transmission_color_texture;
} cgltf_diffuse_transmission;
typedef struct cgltf_anisotropy
{
cgltf_float anisotropy_strength;
@ -525,6 +551,7 @@ typedef struct cgltf_material
cgltf_bool has_sheen;
cgltf_bool has_emissive_strength;
cgltf_bool has_iridescence;
cgltf_bool has_diffuse_transmission;
cgltf_bool has_anisotropy;
cgltf_bool has_dispersion;
cgltf_pbr_metallic_roughness pbr_metallic_roughness;
@ -537,6 +564,7 @@ typedef struct cgltf_material
cgltf_volume volume;
cgltf_emissive_strength emissive_strength;
cgltf_iridescence iridescence;
cgltf_diffuse_transmission diffuse_transmission;
cgltf_anisotropy anisotropy;
cgltf_dispersion dispersion;
cgltf_texture_view normal_texture;
@ -743,6 +771,7 @@ typedef struct cgltf_data
{
cgltf_file_type file_type;
void* file_data;
cgltf_size file_size;
cgltf_asset asset;
@ -844,6 +873,8 @@ void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix)
const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view);
const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_attribute_type type, cgltf_int index);
cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size);
cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size);
cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index);
@ -1071,9 +1102,10 @@ static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* m
return cgltf_result_success;
}
static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data)
static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data, cgltf_size size)
{
(void)file_options;
(void)size;
void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free;
memfree(memory_options->user_data, data);
}
@ -1220,7 +1252,7 @@ cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cg
}
cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read;
void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release;
void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data, cgltf_size size) = options->file.release ? options->file.release : cgltf_default_file_release;
void* file_data = NULL;
cgltf_size file_size = 0;
@ -1234,11 +1266,12 @@ cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cg
if (result != cgltf_result_success)
{
file_release(&options->memory, &options->file, file_data);
file_release(&options->memory, &options->file, file_data, file_size);
return result;
}
(*out_data)->file_data = file_data;
(*out_data)->file_size = file_size;
return cgltf_result_success;
}
@ -1630,8 +1663,8 @@ cgltf_result cgltf_validate(cgltf_data* data)
CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_color && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf);
}
}
@ -1822,7 +1855,7 @@ void cgltf_free(cgltf_data* data)
return;
}
void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release;
void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data, cgltf_size size) = data->file.release ? data->file.release : cgltf_default_file_release;
data->memory.free_func(data->memory.user_data, data->asset.copyright);
data->memory.free_func(data->memory.user_data, data->asset.generator);
@ -1857,7 +1890,7 @@ void cgltf_free(cgltf_data* data)
if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release)
{
file_release(&data->memory, &data->file, data->buffers[i].data);
file_release(&data->memory, &data->file, data->buffers[i].data, data->buffers[i].size);
}
else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free)
{
@ -2096,7 +2129,7 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->extensions_required);
file_release(&data->memory, &data->file, data->file_data);
file_release(&data->memory, &data->file, data->file_data, data->file_size);
data->memory.free_func(data->memory.user_data, data);
}
@ -2312,11 +2345,58 @@ const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view)
return result;
}
const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_attribute_type type, cgltf_int index)
{
for (cgltf_size i = 0; i < prim->attributes_count; ++i)
{
const cgltf_attribute* attr = &prim->attributes[i];
if (attr->type == type && attr->index == index)
return attr->data;
}
return NULL;
}
static const uint8_t* cgltf_find_sparse_index(const cgltf_accessor* accessor, cgltf_size needle)
{
const cgltf_accessor_sparse* sparse = &accessor->sparse;
const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view);
const uint8_t* value_data = cgltf_buffer_view_data(sparse->values_buffer_view);
if (index_data == NULL || value_data == NULL)
return NULL;
index_data += sparse->indices_byte_offset;
value_data += sparse->values_byte_offset;
cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type);
cgltf_size offset = 0;
cgltf_size length = sparse->count;
while (length)
{
cgltf_size rem = length % 2;
length /= 2;
cgltf_size index = cgltf_component_read_index(index_data + (offset + length) * index_stride, sparse->indices_component_type);
offset += index < needle ? length + rem : 0;
}
if (offset == sparse->count)
return NULL;
cgltf_size index = cgltf_component_read_index(index_data + offset * index_stride, sparse->indices_component_type);
return index == needle ? value_data + offset * accessor->stride : NULL;
}
cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size)
{
if (accessor->is_sparse)
{
return 0;
const uint8_t* element = cgltf_find_sparse_index(accessor, index);
if (element)
return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size);
}
if (accessor->buffer_view == NULL)
{
@ -2460,11 +2540,13 @@ cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size i
{
if (accessor->is_sparse)
{
return 0;
const uint8_t* element = cgltf_find_sparse_index(accessor, index);
if (element)
return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size);
}
if (accessor->buffer_view == NULL)
{
memset(out, 0, element_size * sizeof( cgltf_uint ));
memset(out, 0, element_size * sizeof(cgltf_uint));
return 1;
}
const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view);
@ -2480,7 +2562,9 @@ cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size
{
if (accessor->is_sparse)
{
return 0; // This is an error case, but we can't communicate the error with existing interface.
const uint8_t* element = cgltf_find_sparse_index(accessor, index);
if (element)
return cgltf_component_read_index(element, accessor->component_type);
}
if (accessor->buffer_view == NULL)
{
@ -2598,7 +2682,10 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o
return accessor->count;
}
index_count = accessor->count < index_count ? accessor->count : index_count;
cgltf_size numbers_per_element = cgltf_num_components(accessor->type);
cgltf_size available_numbers = accessor->count * numbers_per_element;
index_count = available_numbers < index_count ? available_numbers : index_count;
cgltf_size index_component_size = cgltf_component_size(accessor->component_type);
if (accessor->is_sparse)
@ -2620,15 +2707,23 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o
}
element += accessor->offset;
if (index_component_size == out_component_size && accessor->stride == out_component_size)
if (index_component_size == out_component_size && accessor->stride == out_component_size * numbers_per_element)
{
memcpy(out, element, index_count * index_component_size);
return index_count;
}
// Data couldn't be copied with memcpy due to stride being larger than the component size.
// OR
// The component size of the output array is larger than the component size of the index data, so index data will be padded.
switch (out_component_size)
{
case 1:
for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride)
{
((uint8_t*)out)[index] = (uint8_t)cgltf_component_read_index(element, accessor->component_type);
}
break;
case 2:
for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride)
{
@ -2642,7 +2737,7 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o
}
break;
default:
break;
return 0;
}
return index_count;
@ -4278,6 +4373,52 @@ static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const*
return i;
}
static int cgltf_parse_json_diffuse_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_diffuse_transmission* out_diff_transmission)
{
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i].size;
++i;
// Defaults
cgltf_fill_float_array(out_diff_transmission->diffuse_transmission_color_factor, 3, 1.0f);
out_diff_transmission->diffuse_transmission_factor = 0.f;
for (int j = 0; j < size; ++j)
{
CGLTF_CHECK_KEY(tokens[i]);
if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionFactor") == 0)
{
++i;
out_diff_transmission->diffuse_transmission_factor = cgltf_json_to_float(tokens + i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionTexture") == 0)
{
i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_diff_transmission->diffuse_transmission_texture);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionColorFactor") == 0)
{
i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_diff_transmission->diffuse_transmission_color_factor, 3);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionColorTexture") == 0)
{
i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_diff_transmission->diffuse_transmission_color_texture);
}
else
{
i = cgltf_skip_json(tokens, i + 1);
}
if (i < 0)
{
return i;
}
}
return i;
}
static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_anisotropy* out_anisotropy)
{
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
@ -4406,8 +4547,8 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok
(void)options;
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
out_sampler->wrap_s = 10497;
out_sampler->wrap_t = 10497;
out_sampler->wrap_s = cgltf_wrap_mode_repeat;
out_sampler->wrap_t = cgltf_wrap_mode_repeat;
int size = tokens[i].size;
++i;
@ -4424,28 +4565,28 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok
{
++i;
out_sampler->mag_filter
= cgltf_json_to_int(tokens + i, json_chunk);
= (cgltf_filter_type)cgltf_json_to_int(tokens + i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0)
{
++i;
out_sampler->min_filter
= cgltf_json_to_int(tokens + i, json_chunk);
= (cgltf_filter_type)cgltf_json_to_int(tokens + i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0)
{
++i;
out_sampler->wrap_s
= cgltf_json_to_int(tokens + i, json_chunk);
= (cgltf_wrap_mode)cgltf_json_to_int(tokens + i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0)
{
++i;
out_sampler->wrap_t
= cgltf_json_to_int(tokens + i, json_chunk);
= (cgltf_wrap_mode)cgltf_json_to_int(tokens + i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
@ -4766,6 +4907,11 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to
out_material->has_iridescence = 1;
i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_diffuse_transmission") == 0)
{
out_material->has_diffuse_transmission = 1;
i = cgltf_parse_json_diffuse_transmission(options, tokens, i + 1, json_chunk, &out_material->diffuse_transmission);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_anisotropy") == 0)
{
out_material->has_anisotropy = 1;
@ -4974,6 +5120,10 @@ static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_
{
out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential;
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "COLOR") == 0)
{
out_meshopt_compression->filter = cgltf_meshopt_compression_filter_color;
}
++i;
}
else
@ -5084,6 +5234,12 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const*
out_buffer_view->has_meshopt_compression = 1;
i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression);
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_meshopt_compression") == 0)
{
out_buffer_view->has_meshopt_compression = 1;
out_buffer_view->meshopt_compression.is_khr = 1;
i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression);
}
else
{
i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++]));
@ -6629,6 +6785,9 @@ static int cgltf_fixup_pointers(cgltf_data* data)
CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count);
CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count);
CGLTF_PTRFIXUP(data->materials[i].diffuse_transmission.diffuse_transmission_texture.texture, data->textures, data->textures_count);
CGLTF_PTRFIXUP(data->materials[i].diffuse_transmission.diffuse_transmission_color_texture.texture, data->textures, data->textures_count);
CGLTF_PTRFIXUP(data->materials[i].anisotropy.anisotropy_texture.texture, data->textures, data->textures_count);
}

1565
src/external/cgltf_write.h vendored Normal file

File diff suppressed because it is too large Load Diff

306
src/external/dr_flac.h vendored
View File

@ -1,6 +1,6 @@
/*
FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_flac - v0.13.0 - TBD
dr_flac - v0.13.3 - 2026-01-17
David Reid - mackron@gmail.com
@ -126,7 +126,7 @@ extern "C" {
#define DRFLAC_VERSION_MAJOR 0
#define DRFLAC_VERSION_MINOR 13
#define DRFLAC_VERSION_REVISION 0
#define DRFLAC_VERSION_REVISION 3
#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
#include <stddef.h> /* For size_t. */
@ -331,6 +331,12 @@ typedef struct
*/
drflac_uint32 type;
/* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */
drflac_uint32 rawDataSize;
/* The offset in the stream of the raw data. */
drflac_uint64 rawDataOffset;
/*
A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to
not modify the contents of this buffer. Use the structures below for more meaningful and structured
@ -338,9 +344,6 @@ typedef struct
*/
const void* pRawData;
/* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */
drflac_uint32 rawDataSize;
union
{
drflac_streaminfo streaminfo;
@ -392,6 +395,7 @@ typedef struct
drflac_uint32 colorDepth;
drflac_uint32 indexColorCount;
drflac_uint32 pictureDataSize;
drflac_uint64 pictureDataOffset; /* Offset from the start of the stream. */
const drflac_uint8* pPictureData;
} picture;
} data;
@ -2712,9 +2716,17 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
#if defined(__GNUC__) || defined(__clang__)
#if defined(DRFLAC_X64)
{
/*
A note on lzcnt.
We check for the presence of the lzcnt instruction at runtime before calling this function, but we still generate this code. I have had
a report where the assembler does not recognize the lzcnt instruction. To work around this we are going to use `rep; bsr` instead which
has an identical byte encoding as lzcnt, and should hopefully improve compatibility with older assemblers.
*/
drflac_uint64 r;
__asm__ __volatile__ (
"lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
"rep; bsr{q %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
/*"lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"*/
);
return (drflac_uint32)r;
@ -2723,12 +2735,13 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
{
drflac_uint32 r;
__asm__ __volatile__ (
"lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
"rep; bsr{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
/*"lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"*/
);
return r;
}
#elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */
#elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !(defined(__thumb__) && !defined(__thumb2__)) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */
{
unsigned int r;
__asm__ __volatile__ (
@ -6434,8 +6447,9 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
runningFilePos += 4;
metadata.type = blockType;
metadata.pRawData = NULL;
metadata.rawDataSize = 0;
metadata.rawDataOffset = runningFilePos;
metadata.pRawData = NULL;
switch (blockType)
{
@ -6712,59 +6726,151 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
}
if (onMeta) {
void* pRawData;
const char* pRunningData;
const char* pRunningDataEnd;
drflac_bool32 result = DRFLAC_TRUE;
drflac_uint32 blockSizeRemaining = blockSize;
char* pMime = NULL;
char* pDescription = NULL;
void* pPictureData = NULL;
pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
if (pRawData == NULL) {
return DRFLAC_FALSE;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.type, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.type = drflac__be2host_32(metadata.data.picture.type);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.mimeLength, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.mimeLength = drflac__be2host_32(metadata.data.picture.mimeLength);
pMime = (char*)drflac__malloc_from_callbacks(metadata.data.picture.mimeLength + 1, pAllocationCallbacks); /* +1 for null terminator. */
if (pMime == NULL) {
result = DRFLAC_FALSE;
goto done_flac;
}
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (blockSizeRemaining < metadata.data.picture.mimeLength || onRead(pUserData, pMime, metadata.data.picture.mimeLength) != metadata.data.picture.mimeLength) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= metadata.data.picture.mimeLength;
pMime[metadata.data.picture.mimeLength] = '\0'; /* Null terminate for safety. */
metadata.data.picture.mime = (const char*)pMime;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.descriptionLength, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.descriptionLength = drflac__be2host_32(metadata.data.picture.descriptionLength);
pDescription = (char*)drflac__malloc_from_callbacks(metadata.data.picture.descriptionLength + 1, pAllocationCallbacks); /* +1 for null terminator. */
if (pDescription == NULL) {
result = DRFLAC_FALSE;
goto done_flac;
}
metadata.pRawData = pRawData;
metadata.rawDataSize = blockSize;
pRunningData = (const char*)pRawData;
pRunningDataEnd = (const char*)pRawData + blockSize;
metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
/* Need space for the rest of the block */
if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (blockSizeRemaining < metadata.data.picture.descriptionLength || onRead(pUserData, pDescription, metadata.data.picture.descriptionLength) != metadata.data.picture.descriptionLength) {
result = DRFLAC_FALSE;
goto done_flac;
}
metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
blockSizeRemaining -= metadata.data.picture.descriptionLength;
pDescription[metadata.data.picture.descriptionLength] = '\0'; /* Null terminate for safety. */
metadata.data.picture.description = (const char*)pDescription;
/* Need space for the rest of the block */
if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.width, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
blockSizeRemaining -= 4;
metadata.data.picture.width = drflac__be2host_32(metadata.data.picture.width);
/* Need space for the picture after the block */
if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.height, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.height = drflac__be2host_32(metadata.data.picture.height);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.colorDepth, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.colorDepth = drflac__be2host_32(metadata.data.picture.colorDepth);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.indexColorCount, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.indexColorCount = drflac__be2host_32(metadata.data.picture.indexColorCount);
/* Picture data. */
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.pictureDataSize, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.pictureDataSize = drflac__be2host_32(metadata.data.picture.pictureDataSize);
if (blockSizeRemaining < metadata.data.picture.pictureDataSize) {
result = DRFLAC_FALSE;
goto done_flac;
}
onMeta(pUserDataMD, &metadata);
/* For the actual image data we want to store the offset to the start of the stream. */
metadata.data.picture.pictureDataOffset = runningFilePos + (blockSize - blockSizeRemaining);
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
/*
For the allocation of image data, we can allow memory allocation to fail, in which case we just leave
the pointer as null. If it fails, we need to fall back to seeking past the image data.
*/
#ifndef DR_FLAC_NO_PICTURE_METADATA_MALLOC
pPictureData = drflac__malloc_from_callbacks(metadata.data.picture.pictureDataSize, pAllocationCallbacks);
if (pPictureData != NULL) {
if (onRead(pUserData, pPictureData, metadata.data.picture.pictureDataSize) != metadata.data.picture.pictureDataSize) {
result = DRFLAC_FALSE;
goto done_flac;
}
} else
#endif
{
/* Allocation failed. We need to seek past the picture data. */
if (!onSeek(pUserData, metadata.data.picture.pictureDataSize, DRFLAC_SEEK_CUR)) {
result = DRFLAC_FALSE;
goto done_flac;
}
}
blockSizeRemaining -= metadata.data.picture.pictureDataSize;
(void)blockSizeRemaining;
metadata.data.picture.pPictureData = (const drflac_uint8*)pPictureData;
/* Only fire the callback if we actually have a way to read the image data. We must have either a valid offset, or a valid data pointer. */
if (metadata.data.picture.pictureDataOffset != 0 || metadata.data.picture.pPictureData != NULL) {
onMeta(pUserDataMD, &metadata);
} else {
/* Don't have a valid offset or data pointer, so just pretend we don't have a picture metadata. */
}
done_flac:
drflac__free_from_callbacks(pMime, pAllocationCallbacks);
drflac__free_from_callbacks(pDescription, pAllocationCallbacks);
drflac__free_from_callbacks(pPictureData, pAllocationCallbacks);
if (result != DRFLAC_TRUE) {
return DRFLAC_FALSE;
}
}
} break;
@ -6800,13 +6906,16 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
*/
if (onMeta) {
void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
if (pRawData == NULL) {
return DRFLAC_FALSE;
}
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (pRawData != NULL) {
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
}
} else {
/* Allocation failed. We need to seek past the block. */
if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
}
metadata.pRawData = pRawData;
@ -8699,7 +8808,7 @@ static drflac_bool32 drflac__on_tell_stdio(void* pUserData, drflac_int64* pCurso
DRFLAC_ASSERT(pFileStdio != NULL);
DRFLAC_ASSERT(pCursor != NULL);
#if defined(_WIN32)
#if defined(_WIN32) && !defined(NXDK)
#if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio);
#else
@ -8821,8 +8930,6 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_
DRFLAC_ASSERT(memoryStream != NULL);
newCursor = memoryStream->currentReadPos;
if (origin == DRFLAC_SEEK_SET) {
newCursor = 0;
} else if (origin == DRFLAC_SEEK_CUR) {
@ -11702,58 +11809,43 @@ static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned
{ \
type* pSampleData = NULL; \
drflac_uint64 totalPCMFrameCount; \
type buffer[4096]; \
drflac_uint64 pcmFramesRead; \
size_t sampleDataBufferSize = sizeof(buffer); \
\
DRFLAC_ASSERT(pFlac != NULL); \
\
totalPCMFrameCount = pFlac->totalPCMFrameCount; \
totalPCMFrameCount = 0; \
\
if (totalPCMFrameCount == 0) { \
type buffer[4096]; \
drflac_uint64 pcmFramesRead; \
size_t sampleDataBufferSize = sizeof(buffer); \
pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pSampleData == NULL) { \
goto on_error; \
} \
\
pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pSampleData == NULL) { \
goto on_error; \
} \
while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
type* pNewSampleData; \
size_t newSampleDataBufferSize; \
\
while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
type* pNewSampleData; \
size_t newSampleDataBufferSize; \
\
newSampleDataBufferSize = sampleDataBufferSize * 2; \
pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pNewSampleData == NULL) { \
drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
goto on_error; \
} \
\
sampleDataBufferSize = newSampleDataBufferSize; \
pSampleData = pNewSampleData; \
newSampleDataBufferSize = sampleDataBufferSize * 2; \
pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pNewSampleData == NULL) { \
drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
goto on_error; \
} \
\
DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
totalPCMFrameCount += pcmFramesRead; \
sampleDataBufferSize = newSampleDataBufferSize; \
pSampleData = pNewSampleData; \
} \
\
/* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \
protect those ears from random noise! */ \
DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
} else { \
drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \
goto on_error; /* The decoded data is too big. */ \
} \
\
pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \
if (pSampleData == NULL) { \
goto on_error; \
} \
\
totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
totalPCMFrameCount += pcmFramesRead; \
} \
\
/* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \
protect those ears from random noise! */ \
DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
\
if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
if (channelsOut) *channelsOut = pFlac->channels; \
if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
@ -12077,7 +12169,19 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat
/*
REVISION HISTORY
================
v0.13.0 - TBD
v0.13.3 - 2026-01-17
- Fix a compiler compatibility issue with some inlined assembly.
- Fix a compilation warning.
v0.13.2 - 2025-12-02
- Improve robustness of the parsing of picture metadata to improve support for memory constrained embedded devices.
- Fix a warning about an assigned by unused variable.
- Improvements to drflac_open_and_read_pcm_frames_*() and family to avoid excessively large memory allocations from malformed files.
v0.13.1 - 2025-09-10
- Fix an error with the NXDK build.
v0.13.0 - 2025-07-23
- API CHANGE: Seek origin enums have been renamed to match the naming convention used by other dr_libs libraries:
- drflac_seek_origin_start -> DRFLAC_SEEK_SET
- drflac_seek_origin_current -> DRFLAC_SEEK_CUR

171
src/external/dr_mp3.h vendored
View File

@ -1,6 +1,6 @@
/*
MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_mp3 - v0.7.0 - TBD
dr_mp3 - v0.7.4 - TBD
David Reid - mackron@gmail.com
@ -72,7 +72,7 @@ extern "C" {
#define DRMP3_VERSION_MAJOR 0
#define DRMP3_VERSION_MINOR 7
#define DRMP3_VERSION_REVISION 0
#define DRMP3_VERSION_REVISION 4
#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
#include <stddef.h> /* For size_t. */
@ -257,16 +257,45 @@ typedef struct
Low Level Push API
==================
*/
#define DRMP3_MAX_BITRESERVOIR_BYTES 511
#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */
#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */
typedef struct
{
int frame_bytes, channels, sample_rate, layer, bitrate_kbps;
} drmp3dec_frame_info;
typedef struct
{
const drmp3_uint8 *buf;
int pos, limit;
} drmp3_bs;
typedef struct
{
const drmp3_uint8 *sfbtab;
drmp3_uint16 part_23_length, big_values, scalefac_compress;
drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
} drmp3_L3_gr_info;
typedef struct
{
drmp3_bs bs;
drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
drmp3_L3_gr_info gr_info[4];
float grbuf[2][576], scf[40], syn[18 + 15][2*32];
drmp3_uint8 ist_pos[2][39];
} drmp3dec_scratch;
typedef struct
{
float mdct_overlap[2][9*32], qmf_state[15*2*32];
int reserv, free_format_bytes;
drmp3_uint8 header[4], reserv_buf[511];
drmp3dec_scratch scratch;
} drmp3dec;
/* Initializes a low level decoder. */
@ -592,14 +621,10 @@ DRMP3_API const char* drmp3_version_string(void)
#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */
#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
#define DRMP3_MAX_FRAME_SYNC_MATCHES 10
#endif
#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */
#define DRMP3_MAX_BITRESERVOIR_BYTES 511
#define DRMP3_SHORT_BLOCK_TYPE 2
#define DRMP3_STOP_BLOCK_TYPE 3
#define DRMP3_MODE_MONO 3
@ -632,8 +657,10 @@ DRMP3_API const char* drmp3_version_string(void)
#if !defined(DR_MP3_NO_SIMD)
#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC))
/* x64 always have SSE2, arm64 always have neon, no need for generic code */
#if !defined(DR_MP3_ONLY_SIMD) && ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)))
#define DR_MP3_ONLY_SIMD
#endif
#if !defined(DR_MP3_ONLY_SIMD) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC))
#define DR_MP3_ONLY_SIMD
#endif
@ -655,7 +682,7 @@ DRMP3_API const char* drmp3_version_string(void)
#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
typedef __m128 drmp3_f4;
#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)
#if (defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)) && !defined(__clang__)
#define drmp3_cpuid __cpuid
#else
static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType)
@ -779,11 +806,7 @@ static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_ar
#define DRMP3_FREE(p) free((p))
#endif
typedef struct
{
const drmp3_uint8 *buf;
int pos, limit;
} drmp3_bs;
typedef struct
{
@ -796,24 +819,6 @@ typedef struct
drmp3_uint8 tab_offset, code_tab_width, band_count;
} drmp3_L12_subband_alloc;
typedef struct
{
const drmp3_uint8 *sfbtab;
drmp3_uint16 part_23_length, big_values, scalefac_compress;
drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
} drmp3_L3_gr_info;
typedef struct
{
drmp3_bs bs;
drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
drmp3_L3_gr_info gr_info[4];
float grbuf[2][576], scf[40], syn[18 + 15][2*32];
drmp3_uint8 ist_pos[2][39];
} drmp3dec_scratch;
static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes)
{
bs->buf = data;
@ -1227,6 +1232,14 @@ static float drmp3_L3_ldexp_q2(float y, int exp_q2)
return y;
}
/*
I've had reports of GCC 14 throwing an incorrect -Wstringop-overflow warning here. This is an attempt
to silence this warning.
*/
#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch)
{
static const drmp3_uint8 g_scf_partitions[3][28] = {
@ -1288,6 +1301,9 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is
scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
}
}
#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
static const float g_drmp3_pow43[129 + 16] = {
0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f,
@ -2299,7 +2315,6 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m
int i = 0, igr, frame_size = 0, success = 1;
const drmp3_uint8 *hdr;
drmp3_bs bs_frame[1];
drmp3dec_scratch scratch;
if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3))
{
@ -2336,23 +2351,23 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m
if (info->layer == 3)
{
int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);
int main_data_begin = drmp3_L3_read_side_info(bs_frame, dec->scratch.gr_info, hdr);
if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
{
drmp3dec_init(dec);
return 0;
}
success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
success = drmp3_L3_restore_reservoir(dec, bs_frame, &dec->scratch, main_data_begin);
if (success && pcm != NULL)
{
for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
{
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
drmp3_L3_decode(dec, &dec->scratch, dec->scratch.gr_info + igr*info->channels, info->channels);
drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]);
}
}
drmp3_L3_save_reservoir(dec, &scratch);
drmp3_L3_save_reservoir(dec, &dec->scratch);
} else
{
#ifdef DR_MP3_ONLY_MP3
@ -2366,15 +2381,15 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m
drmp3_L12_read_scale_info(hdr, bs_frame, sci);
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
for (i = 0, igr = 0; igr < 3; igr++)
{
if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
if (12 == (i += drmp3_L12_dequantize_granule(dec->scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
{
i = 0;
drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
drmp3_L12_apply_scf_384(sci, sci->scf + igr, dec->scratch.grbuf[0]);
drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]);
DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
}
if (bs_frame->pos > bs_frame->limit)
@ -3005,23 +3020,27 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
((drmp3_uint32)ape[26] << 16) |
((drmp3_uint32)ape[27] << 24);
streamEndOffset -= 32 + tagSize;
streamLen -= 32 + tagSize;
/* Fire a metadata callback for the APE data. Must include both the main content and footer. */
if (onMeta != NULL) {
/* We first need to seek to the start of the APE tag. */
if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) {
size_t apeTagSize = (size_t)tagSize + 32;
drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks);
if (pTagData != NULL) {
if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) {
drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize);
}
if (32 + tagSize < streamLen) {
streamEndOffset -= 32 + tagSize;
streamLen -= 32 + tagSize;
/* Fire a metadata callback for the APE data. Must include both the main content and footer. */
if (onMeta != NULL) {
/* We first need to seek to the start of the APE tag. */
if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) {
size_t apeTagSize = (size_t)tagSize + 32;
drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks);
if (pTagData != NULL) {
if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) {
drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize);
}
drmp3_free(pTagData, pAllocationCallbacks);
drmp3_free(pTagData, pAllocationCallbacks);
}
}
}
} else {
/* The tag size is larger than the stream. Invalid APE tag. */
}
}
}
@ -3153,7 +3172,6 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
{
drmp3_bs bs;
drmp3_L3_gr_info grInfo[4];
const drmp3_uint8* pTagData = pFirstFrameData;
drmp3_bs_init(&bs, pFirstFrameData + DRMP3_HDR_SIZE, firstFrameInfo.frame_bytes - DRMP3_HDR_SIZE);
@ -3164,6 +3182,7 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
if (drmp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) {
drmp3_bool32 isXing = DRMP3_FALSE;
drmp3_bool32 isInfo = DRMP3_FALSE;
const drmp3_uint8* pTagData;
const drmp3_uint8* pTagDataBeg;
pTagDataBeg = pFirstFrameData + DRMP3_HDR_SIZE + (bs.pos/8);
@ -3246,6 +3265,13 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
/* The start offset needs to be moved to the end of this frame so it's not included in any audio processing after seeking. */
pMP3->streamStartOffset += (drmp3_uint32)(firstFrameInfo.frame_bytes);
pMP3->streamCursor = pMP3->streamStartOffset;
/*
The internal decoder needs to be reset to clear out any state. If we don't reset this state, it's possible for
there to be inconsistencies in the number of samples read when reading to the end of the stream depending on
whether or not the caller seeks to the start of the stream.
*/
drmp3dec_init(&pMP3->decoder);
}
} else {
/* Failed to read the side info. */
@ -3307,8 +3333,6 @@ static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3
DRMP3_ASSERT(pMP3 != NULL);
newCursor = pMP3->memory.currentReadPos;
if (origin == DRMP3_SEEK_SET) {
newCursor = 0;
} else if (origin == DRMP3_SEEK_CUR) {
@ -3981,7 +4005,7 @@ static drmp3_bool32 drmp3__on_tell_stdio(void* pUserData, drmp3_int64* pCursor)
DRMP3_ASSERT(pFileStdio != NULL);
DRMP3_ASSERT(pCursor != NULL);
#if defined(_WIN32)
#if defined(_WIN32) && !defined(NXDK)
#if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio);
#else
@ -4780,6 +4804,8 @@ static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig,
pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
if (pNewFrames == NULL) {
drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
pFrames = NULL;
totalFramesRead = 0;
break;
}
@ -4847,6 +4873,8 @@ static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pC
pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
if (pNewFrames == NULL) {
drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
pFrames = NULL;
totalFramesRead = 0;
break;
}
@ -4981,7 +5009,24 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3
/*
REVISION HISTORY
================
v0.7.0 - TBD
v0.7.4 - TBD
- Improvements to SIMD detection.
v0.7.3 - 2026-01-17
- Fix an error in drmp3_open_and_read_pcm_frames_s16() and family when memory allocation fails.
- Fix some compilation warnings.
v0.7.2 - 2025-12-02
- Reduce stack space to improve robustness on embedded systems.
- Fix a compilation error with MSVC Clang toolset relating to cpuid.
- Fix an error with APE tag parsing.
v0.7.1 - 2025-09-10
- Silence a warning with GCC.
- Fix an error with the NXDK build.
- Fix a decoding inconsistency when seeking. Prior to this change, reading to the end of the stream immediately after initializing will result in a different number of samples read than if the stream is seeked to the start and read to the end.
v0.7.0 - 2025-07-23
- The old `DRMP3_IMPLEMENTATION` has been removed. Use `DR_MP3_IMPLEMENTATION` instead. The reason for this change is that in the future everything will eventually be using the underscored naming convention in the future, so `drmp3` will become `dr_mp3`.
- API CHANGE: Seek origins have been renamed to match the naming convention used by dr_wav and my other libraries.
- drmp3_seek_origin_start -> DRMP3_SEEK_SET

133
src/external/dr_wav.h vendored
View File

@ -1,6 +1,6 @@
/*
WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_wav - v0.14.0 - TBD
dr_wav - v0.14.5 - 2026-03-03
David Reid - mackron@gmail.com
@ -147,7 +147,7 @@ extern "C" {
#define DRWAV_VERSION_MAJOR 0
#define DRWAV_VERSION_MINOR 14
#define DRWAV_VERSION_REVISION 0
#define DRWAV_VERSION_REVISION 5
#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
#include <stddef.h> /* For size_t. */
@ -2189,6 +2189,22 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
drwav_uint32 iSampleLoop;
drwav_uint32 loopCount;
drwav_uint32 calculatedLoopCount;
/*
When we calcualted the amount of memory required for the "smpl" chunk we excluded the chunk entirely
if the loop count in the header did not match with the calculated count based on the size of the
chunk. When this happens, the second stage will still hit this path but the `pMetadata` will be
non-null, but will either be pointing at the very end of the allocation or at the start of another
chunk. We need to check the loop counts for consistency *before* dereferencing the pMetadata object
so it's consistent with how we do it in the first stage.
*/
loopCount = drwav_bytes_to_u32(smplHeaderData + 28);
calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES;
if (loopCount != calculatedLoopCount) {
return totalBytesRead;
}
pMetadata->type = drwav_metadata_type_smpl;
pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
@ -2205,7 +2221,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
The loop count needs to be validated against the size of the chunk for safety so we don't
attempt to read over the boundary of the chunk.
*/
if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) {
if (pMetadata->data.smpl.sampleLoopCount == calculatedLoopCount) {
pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);
for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
@ -2230,6 +2246,15 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
}
} else {
/*
Getting here means the loop count in the header does not match up with the size of the
chunk. Clear out the data to zero just to be safe.
This should never actually get hit because we check for it above, but keeping this here
for added safety.
*/
DRWAV_ZERO_OBJECT(&pMetadata->data.smpl);
}
}
@ -3605,7 +3630,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
we'll need to abort because we can't be doing a backwards seek back to the SSND chunk in order to read the
data. For this reason, this configuration of AIFF files are not supported with sequential mode.
*/
return DRWAV_FALSE;
return DRWAV_FALSE;
}
} else {
chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
@ -5285,7 +5310,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pC
DRWAV_ASSERT(pFileStdio != NULL);
DRWAV_ASSERT(pCursor != NULL);
#if defined(_WIN32)
#if defined(_WIN32) && !defined(NXDK)
#if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio);
#else
@ -5492,8 +5517,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, dr
DRWAV_ASSERT(pWav != NULL);
newCursor = pWav->memoryStream.currentReadPos;
if (origin == DRWAV_SEEK_SET) {
newCursor = 0;
} else if (origin == DRWAV_SEEK_CUR) {
@ -5566,8 +5589,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs
DRWAV_ASSERT(pWav != NULL);
newCursor = pWav->memoryStreamWrite.currentWritePos;
if (origin == DRWAV_SEEK_SET) {
newCursor = 0;
} else if (origin == DRWAV_SEEK_CUR) {
@ -5576,7 +5597,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs
newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize;
} else {
DRWAV_ASSERT(!"Invalid seek origin");
return DRWAV_INVALID_ARGS;
return DRWAV_FALSE;
}
newCursor += offset;
@ -6258,12 +6279,12 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
{
drwav_uint64 totalFramesRead = 0;
static drwav_int32 adaptationTable[] = {
static const drwav_int32 adaptationTable[] = {
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
static const drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
static const drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
DRWAV_ASSERT(pWav != NULL);
DRWAV_ASSERT(framesToRead > 0);
@ -6292,7 +6313,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
pWav->msadpcm.cachedFrameCount = 2;
/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) {
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Invalid file. */
}
} else {
@ -6319,7 +6340,8 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
pWav->msadpcm.cachedFrameCount = 2;
/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table) ||
pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Invalid file. */
}
}
@ -6373,15 +6395,17 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
drwav_int32 newSample0;
drwav_int32 newSample1;
/* The predictor is read from the file and then indexed into a table. Check that it's in bounds. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead;
}
newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample0 += nibble0 * pWav->msadpcm.delta[0];
newSample0 = drwav_clamp(newSample0, -32768, 32767);
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample0;
@ -6390,15 +6414,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
newSample1 += nibble1 * pWav->msadpcm.delta[0];
newSample1 = drwav_clamp(newSample1, -32768, 32767);
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample1;
pWav->msadpcm.cachedFrames[2] = newSample0;
pWav->msadpcm.cachedFrames[3] = newSample1;
pWav->msadpcm.cachedFrameCount = 2;
@ -6408,28 +6428,30 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
drwav_int32 newSample1;
/* Left. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Out of bounds. Invalid file. */
}
newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample0 += nibble0 * pWav->msadpcm.delta[0];
newSample0 = drwav_clamp(newSample0, -32768, 32767);
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample0;
/* Right. */
if (pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Out of bounds. Invalid file. */
}
newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
newSample1 += nibble1 * pWav->msadpcm.delta[1];
newSample1 = drwav_clamp(newSample1, -32768, 32767);
pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
if (pWav->msadpcm.delta[1] < 16) {
pWav->msadpcm.delta[1] = 16;
}
pWav->msadpcm.delta[1] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8, 16, 0x7FFFFFFF);
pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
pWav->msadpcm.prevFrames[1][1] = newSample1;
@ -6451,12 +6473,12 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin
drwav_uint64 totalFramesRead = 0;
drwav_uint32 iChannel;
static drwav_int32 indexTable[16] = {
static const drwav_int32 indexTable[16] = {
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};
static drwav_int32 stepTable[89] = {
static const drwav_int32 stepTable[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
@ -6606,7 +6628,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin
#ifndef DR_WAV_NO_CONVERSION_API
static unsigned short g_drwavAlawTable[256] = {
static const unsigned short g_drwavAlawTable[256] = {
0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
@ -6625,7 +6647,7 @@ static unsigned short g_drwavAlawTable[256] = {
0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
};
static unsigned short g_drwavMulawTable[256] = {
static const unsigned short g_drwavMulawTable[256] = {
0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
@ -8053,6 +8075,12 @@ DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, uns
DRWAV_ASSERT(pWav != NULL);
/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int16)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
@ -8095,6 +8123,12 @@ DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned
DRWAV_ASSERT(pWav != NULL);
/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(float)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
@ -8137,6 +8171,12 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns
DRWAV_ASSERT(pWav != NULL);
/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int32)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
@ -8517,7 +8557,24 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
/*
REVISION HISTORY
================
v0.14.0 - TBD
v0.14.5 - 2026-03-03
- Fix a crash when loading files with a malformed "smpl" chunk.
- Fix a signed overflow bug with the MS-ADPCM decoder.
v0.14.4 - 2026-01-17
- Fix some compilation warnings.
v0.14.3 - 2025-12-14
- Fix a possible out-of-bounds read when reading from MS-ADPCM encoded files.
- Fix a possible integer overflow error.
v0.14.2 - 2025-12-02
- Fix a compilation warning.
v0.14.1 - 2025-09-10
- Fix an error with the NXDK build.
v0.14.0 - 2025-07-23
- API CHANGE: Seek origin enums have been renamed to the following:
- drwav_seek_origin_start -> DRWAV_SEEK_SET
- drwav_seek_origin_current -> DRWAV_SEEK_CUR

53
src/external/m3d.h vendored
View File

@ -236,7 +236,8 @@ typedef struct {
#ifdef M3D_ASCII
#define M3D_PROPERTYDEF(f,i,n) { (f), (i), (char*)(n) }
char *key;
#else
#endif
#ifndef M3D_ASCII
#define M3D_PROPERTYDEF(f,i,n) { (f), (i) }
#endif
} m3dpd_t;
@ -414,7 +415,8 @@ typedef struct {
#ifdef M3D_ASCII
#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (char*)(n), (p), { (a), (b), (c), (d), (e), (f), (g), (h) } }
char *key;
#else
#endif
#ifndef M3D_ASCII
#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (p), { (a), (b), (c), (d), (e), (f), (g), (h) } }
#endif
uint8_t p;
@ -674,6 +676,10 @@ static m3dcd_t m3d_commandtypes[] = {
#include <stdlib.h>
#include <string.h>
/* we'll need this with M3D_NOTEXTURE */
char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
#ifndef M3D_NOTEXTURE
#if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H)
/* PNG decompressor from
@ -1868,6 +1874,11 @@ static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp,
#if !defined(M3D_NOIMPORTER) && defined(STBI_INCLUDE_STB_IMAGE_H) && !defined(STB_IMAGE_IMPLEMENTATION)
#error "stb_image.h included without STB_IMAGE_IMPLEMENTATION. Sorry, we need some stuff defined inside the ifguard for proper integration"
#endif
#else
#if !defined(STBI_INCLUDE_STB_IMAGE_H) || defined(STBI_NO_ZLIB)
#error "stb_image.h not included or STBI_NO_ZLIB defined. Sorry, we need its zlib implementation for proper integration"
#endif
#endif /* M3D_NOTEXTURE */
#if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H)
/* zlib_compressor from
@ -2168,9 +2179,11 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char
unsigned int i, len = 0;
unsigned char *buff = NULL;
char *fn2;
#ifndef M3D_NOTEXTURE
unsigned int w, h;
stbi__context s;
stbi__result_info ri;
#endif
/* failsafe */
if(!fn || !*fn) return M3D_UNDEF;
@ -2209,13 +2222,17 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char
if(!model->texture) {
if(buff && freecb) (*freecb)(buff);
model->errcode = M3D_ERR_ALLOC;
model->numtexture = 0;
return M3D_UNDEF;
}
memset(&model->texture[i], 0, sizeof(m3dtx_t));
model->texture[i].name = fn;
model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL;
if(buff) {
if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') {
s.read_from_callbacks = 0;
#ifndef M3D_NOTEXTURE
/* return pixel buffer of the decoded texture */
memset(&s, 0, sizeof(s));
memset(&ri, 0, sizeof(ri));
s.img_buffer = s.img_buffer_original = (unsigned char *) buff;
s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len;
/* don't use model->texture[i].w directly, it's a uint16_t */
@ -2225,6 +2242,16 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char
model->texture[i].w = w;
model->texture[i].h = h;
model->texture[i].f = (uint8_t)len;
#else
/* return only the raw undecoded texture */
if((model->texture[i].d = (uint8_t*)M3D_MALLOC(len))) {
memcpy(model->texture[i].d, buff, len);
model->texture[i].w = len & 0xffff;
model->texture[i].h = (len >> 16) & 0xffff;
model->texture[i].f = 0;
} else
model->errcode = M3D_ERR_ALLOC;
#endif
} else {
#ifdef M3D_TX_INTERP
if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) {
@ -4280,7 +4307,7 @@ m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec)
if(l != msec) {
model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t));
if(!model->vertex) {
free(ret);
M3D_FREE(ret);
model->errcode = M3D_ERR_ALLOC;
return NULL;
}
@ -4492,7 +4519,7 @@ void m3d_free(m3d_t *model)
if(model->label) M3D_FREE(model->label);
if(model->inlined) M3D_FREE(model->inlined);
if(model->extra) M3D_FREE(model->extra);
free(model);
M3D_FREE(model);
}
#endif
@ -4568,15 +4595,15 @@ static uint32_t _m3d_stridx(m3dstr_t *str, uint32_t numstr, char *s)
safe = _m3d_safestr(s, 0);
if(!safe) return 0;
if(!*safe) {
free(safe);
M3D_FREE(safe);
return 0;
}
for(i = 0; i < numstr; i++)
if(!strcmp(str[i].str, s)) {
free(safe);
M3D_FREE(safe);
return str[i].offs;
}
free(safe);
M3D_FREE(safe);
}
return 0;
}
@ -4889,7 +4916,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size
if(cmd->type == m3dc_mesh) {
if(numgrp + 2 < maxgrp) {
maxgrp += 1024;
grpidx = (uint32_t*)realloc(grpidx, maxgrp * sizeof(uint32_t));
grpidx = (uint32_t*)M3D_REALLOC(grpidx, maxgrp * sizeof(uint32_t));
if(!grpidx) goto memerr;
if(!numgrp) {
grpidx[0] = 0;
@ -5194,7 +5221,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx);
if(sa) M3D_FREE(sa);
if(sd) M3D_FREE(sd);
if(out) M3D_FREE(out);
if(opa) free(opa);
if(opa) M3D_FREE(opa);
if(h) M3D_FREE(h);
M3D_LOG("Out of memory");
model->errcode = M3D_ERR_ALLOC;
@ -6285,7 +6312,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx);
if(skin) M3D_FREE(skin);
if(str) M3D_FREE(str);
if(vrtx) M3D_FREE(vrtx);
if(opa) free(opa);
if(opa) M3D_FREE(opa);
if(h) M3D_FREE(h);
return out;
}
@ -6310,7 +6337,7 @@ namespace M3D {
public:
Model() {
this->model = (m3d_t*)malloc(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t));
this->model = (m3d_t*)M3D_MALLOC(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t));
}
Model(_unused const std::string &data, _unused m3dread_t ReadFileCB,
_unused m3dfree_t FreeCB, _unused M3D::Model mtllib) {

60
src/external/qoa.h vendored
View File

@ -31,7 +31,7 @@ struct {
struct {
char magic[4]; // magic bytes "qoaf"
uint32_t samples; // samples per channel in this file
} file_header;
} file_header;
struct {
struct {
@ -39,12 +39,12 @@ struct {
uint24_t samplerate; // samplerate in hz
uint16_t fsamples; // samples per channel in this frame
uint16_t fsize; // frame size (includes this header)
} frame_header;
} frame_header;
struct {
int16_t history[4]; // most recent last
int16_t weights[4]; // most recent last
} lms_state[num_channels];
} lms_state[num_channels];
qoa_slice_t slices[256][num_channels];
@ -66,7 +66,7 @@ frame may contain between 1 .. 256 (inclusive) slices per channel. The last
slice (for each channel) in the last frame may contain less than 20 samples; the
slice still must be 8 bytes wide, with the unused samples zeroed out.
Channels are interleaved per slice. E.g. for 2 channel stereo:
Channels are interleaved per slice. E.g. for 2 channel stereo:
slice[0] = L, slice[1] = R, slice[2] = L, slice[3] = R ...
A valid QOA file or stream must have at least one frame. Each frame must contain
@ -74,7 +74,7 @@ at least one channel and one sample with a samplerate between 1 .. 16777215
(inclusive).
If the total number of samples is not known by the encoder, the samples in the
file header may be set to 0x00000000 to indicate that the encoder is
file header may be set to 0x00000000 to indicate that the encoder is
"streaming". In a streaming context, the samplerate and number of channels may
differ from frame to frame. For static files (those with samples set to a
non-zero value), each frame must have the same number of channels and same
@ -88,15 +88,15 @@ counts 1 .. 8 is:
1. Mono
2. L, R
3. L, R, C
4. FL, FR, B/SL, B/SR
5. FL, FR, C, B/SL, B/SR
3. L, R, C
4. FL, FR, B/SL, B/SR
5. FL, FR, C, B/SL, B/SR
6. FL, FR, C, LFE, B/SL, B/SR
7. FL, FR, C, LFE, B, SL, SR
7. FL, FR, C, LFE, B, SL, SR
8. FL, FR, C, LFE, BL, BR, SL, SR
QOA predicts each audio sample based on the previously decoded ones using a
"Sign-Sign Least Mean Squares Filter" (LMS). This prediction plus the
"Sign-Sign Least Mean Squares Filter" (LMS). This prediction plus the
dequantized residual forms the final output sample.
*/
@ -178,9 +178,9 @@ typedef unsigned long long qoa_uint64_t;
/* The quant_tab provides an index into the dequant_tab for residuals in the
range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at
the higher end. Note that the residual zero is identical to the lowest positive
value. This is mostly fine, since the qoa_div() function always rounds away
range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at
the higher end. Note that the residual zero is identical to the lowest positive
value. This is mostly fine, since the qoa_div() function always rounds away
from zero. */
static const int qoa_quant_tab[17] = {
@ -193,8 +193,8 @@ static const int qoa_quant_tab[17] = {
/* We have 16 different scalefactors. Like the quantized residuals these become
less accurate at the higher end. In theory, the highest scalefactor that we
would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we
rely on the LMS filter to predict samples accurately enough that a maximum
residual of one quarter of the 16 bit range is sufficient. I.e. with the
rely on the LMS filter to predict samples accurately enough that a maximum
residual of one quarter of the 16 bit range is sufficient. I.e. with the
scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14.
The scalefactor values are computed as:
@ -205,9 +205,9 @@ static const int qoa_scalefactor_tab[16] = {
};
/* The reciprocal_tab maps each of the 16 scalefactors to their rounded
reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in
the encoder with just one multiplication instead of an expensive division. We
/* The reciprocal_tab maps each of the 16 scalefactors to their rounded
reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in
the encoder with just one multiplication instead of an expensive division. We
do this in .16 fixed point with integers, instead of floats.
The reciprocal_tab is computed as:
@ -218,11 +218,11 @@ static const int qoa_reciprocal_tab[16] = {
};
/* The dequant_tab maps each of the scalefactors and quantized residuals to
/* The dequant_tab maps each of the scalefactors and quantized residuals to
their unscaled & dequantized version.
Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4
instead of 1. The dequant_tab assumes the following dequantized values for each
instead of 1. The dequant_tab assumes the following dequantized values for each
of the quant_tab indices and is computed as:
float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7};
dequant_tab[s][q] <- round_ties_away_from_zero(scalefactor_tab[s] * dqt[q])
@ -258,7 +258,7 @@ adjusting 4 weights based on the residual of the previous prediction.
The next sample is predicted as the sum of (weight[i] * history[i]).
The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or
subtracts the residual to each weight, based on the corresponding sample from
subtracts the residual to each weight, based on the corresponding sample from
the history. This, surprisingly, is sufficient to get worthwhile predictions.
This is all done with fixed point integers. Hence the right-shifts when updating
@ -285,8 +285,8 @@ static void qoa_lms_update(qoa_lms_t *lms, int sample, int residual) {
}
/* qoa_div() implements a rounding division, but avoids rounding to zero for
small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still
/* qoa_div() implements a rounding division, but avoids rounding to zero for
small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still
returns as 0, which is handled in the qoa_quant_tab[].
qoa_div() takes an index into the .16 fixed point qoa_reciprocal_tab as an
argument, so it can do the division with a cheaper integer multiplication. */
@ -385,10 +385,10 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
for (unsigned int c = 0; c < channels; c++) {
int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index);
int slice_start = sample_index * channels + c;
int slice_end = (sample_index + slice_len) * channels + c;
int slice_end = (sample_index + slice_len) * channels + c;
/* Brute for search for the best scalefactor. Just go through all
16 scalefactors, encode all samples for the current slice and
/* Brute force search for the best scalefactor. Just go through all
16 scalefactors, encode all samples for the current slice and
meassure the total squared error. */
qoa_uint64_t best_rank = -1;
#ifdef QOA_RECORD_TOTAL_ERROR
@ -402,7 +402,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
/* There is a strong correlation between the scalefactors of
neighboring slices. As an optimization, start testing
the best scalefactor of the previous slice first. */
int scalefactor = (sfi + prev_scalefactor[c]) % 16;
int scalefactor = (sfi + prev_scalefactor[c]) & (16 - 1);
/* We have to reset the LMS state to the last known good one
before trying each scalefactor, as each pass updates the LMS
@ -500,7 +500,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len)
num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */
num_slices * 8 * qoa->channels; /* 8 byte slices */
unsigned char *bytes = (unsigned char *)QOA_MALLOC(encoded_size);
unsigned char *bytes = QOA_MALLOC(encoded_size);
for (unsigned int c = 0; c < qoa->channels; c++) {
/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the
@ -657,7 +657,7 @@ short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) {
/* Calculate the required size of the sample buffer and allocate */
int total_samples = qoa->samples * qoa->channels;
short *sample_data = (short *)QOA_MALLOC(total_samples * sizeof(short));
short *sample_data = QOA_MALLOC(total_samples * sizeof(short));
unsigned int sample_index = 0;
unsigned int frame_len;
@ -733,7 +733,7 @@ void *qoa_read(const char *filename, qoa_desc *qoa) {
bytes_read = fread(data, 1, size, f);
fclose(f);
sample_data = qoa_decode((const unsigned char *)data, bytes_read, qoa);
sample_data = qoa_decode(data, bytes_read, qoa);
QOA_FREE(data);
return sample_data;
}

4
src/external/qoi.h vendored
View File

@ -427,7 +427,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
run = 0;
}
index_pos = QOI_COLOR_HASH(px) % 64;
index_pos = QOI_COLOR_HASH(px) & (64 - 1);
if (index[index_pos].v == px.v) {
bytes[p++] = QOI_OP_INDEX | index_pos;
@ -574,7 +574,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
run = (b1 & 0x3f);
}
index[QOI_COLOR_HASH(px) % 64] = px;
index[QOI_COLOR_HASH(px) & (64 - 1)] = px;
}
pixels[px_pos + 0] = px.rgba.r;

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
* #define SUPPORT_FILEFORMAT_MTL
* #define SUPPORT_FILEFORMAT_IQM
* #define SUPPORT_FILEFORMAT_GLTF
* #define SUPPORT_FILEFORMAT_GLTF_WRITE
* #define SUPPORT_FILEFORMAT_VOX
* #define SUPPORT_FILEFORMAT_M3D
* Selected desired fileformats to be supported for model data loading
@ -71,6 +72,11 @@
#define CGLTF_IMPLEMENTATION
#include "external/cgltf.h" // glTF file format loading
#endif
#if SUPPORT_FILEFORMAT_GLTF_WRITE
// NOTE: No need for custom allocators, memory buffer provided
#define CGLTF_WRITE_IMPLEMENTATION
#include "external/cgltf_write.h" // glTF file format writing
#endif
#if SUPPORT_FILEFORMAT_VOX
#define VOX_MALLOC RL_MALLOC
@ -2017,6 +2023,19 @@ bool ExportMesh(Mesh mesh, const char *fileName)
RL_FREE(txtData);
}
else if (IsFileExtension(fileName, ".gltf")) // Or .glb
{
// TODO: Implement gltf/glb support
/*
cgltf_size expected = cgltf_write(options, NULL, 0, data);
char *buffer = (char *)RL_CALLOC(expected, 0);
cgltf_size actual = cgltf_write(options, buffer, expected, data);
// NOTE: cgltf_write() includes a NULL terminator that should be ommited in case of a .glb
if (options->type == cgltf_file_type_glb) cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size);
else SaveFileText(fileName, buffer); // Write a plain JSON file
*/
}
else if (IsFileExtension(fileName, ".raw"))
{
// TODO: Support additional file formats to export mesh vertex data
@ -5265,7 +5284,7 @@ static cgltf_result LoadFileGLTFCallback(const struct cgltf_memory_options *memo
}
// Release file data callback for cgltf
static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data)
static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data, cgltf_size size)
{
UnloadFileData((unsigned char *)data);
}